npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

imessage-html-recreation

v0.1.0

Published

Composable iMessage-style HTML components. Accurate bubble geometry, optional conversation UI, and no runtime dependencies.

Readme

imessage-html-recreation

Composable iMessage-style HTML components with continuous bubble tails, selectable text, and independently optional conversation UI. MIT-licensed code; no runtime dependencies. Version 0.1.0 is prepared for release and has not yet been published to npm.

Copy this folder to your public assets as imessage, or install the local package with npm install ./packages/imessage-html-recreation.

<script type="module" src="./imessage/index.js"></script>
<imessage-conversation>
  <imessage-bubble direction="received">How much do we owe you?</imessage-bubble>
  <imessage-bubble direction="sent" status="Read 12:53 PM">$17.64 each!</imessage-bubble>
</imessage-conversation>

In a bundler, import imessage-html-recreation once from a browser entry. The module can be imported during SSR, but creating elements requires the browser. Preserve intentional newlines and avoid indentation inside short messages.

Elements

| Element | Attributes / purpose | | --- | --- | | imessage-conversation | theme="dark" or light; optional named header, status-bar, composer, keyboard slots | | imessage-bubble | direction="sent" or received, tail="false", status, width, group="first/middle/last" | | imessage-header | name, avatar, back="false", video="false", unread; optional avatar slot | | imessage-composer | placeholder, value, plus="false", microphone="false" | | imessage-timestamp | Centered slotted timestamp | | imessage-thread | Curved reply connector | | imessage-status-bar | time, island="false" | | imessage-keyboard | Optional QWERTY keyboard that emits key/action events |

Leave out an element to disable it. Attributes are strings; use ="false" to turn individual flags off.

<imessage-conversation theme="dark">
  <imessage-header slot="header" name="Alex" avatar="/avatar.jpg" back="false"></imessage-header>
  <imessage-bubble direction="received">Hey!</imessage-bubble>
  <imessage-bubble direction="sent">Hello!</imessage-bubble>
  <imessage-composer slot="composer"></imessage-composer>
</imessage-conversation>

Customize

imessage-conversation {
  --im-background: #000;
  --im-sent-top: #2995ff;
  --im-sent-bottom: #078fff;
  --im-received: #262628;
  --im-font-size: 17px;
  --im-line-height: 20px;
  --im-max-width: 78%;
  --im-inset-x: 20px;
  --im-radius: 22;
}

Other properties: --im-font, --im-letter-spacing, --im-sent-letter-spacing, --im-incoming-text, --im-outgoing-text, --im-muted, --im-padding-x, --im-padding-y, --im-message-gap, --im-inset-top, --im-inset-bottom, --im-thread-color, --im-thread-height, --im-glass, --im-chrome-text, --im-input, --im-input-border, and --im-composer-inset.

React

The optional wrapper requires React 18+. It registers custom elements after mount, forwards refs, and maps booleans to string attributes.

import { Conversation, Bubble, Header, Composer } from 'imessage-html-recreation/react';
<Conversation>
  <Header slot="header" name="Alex" back={false} />
  <Bubble direction="received">Hey!</Bubble>
  <Bubble direction="sent">Hello!</Bubble>
  <Composer slot="composer" onSend={({ text }) => console.log(text)} />
</Conversation>

Also exports Timestamp, Thread, StatusBar, and Keyboard. Add 'use client' when your framework requires it.

Events and JavaScript

imessage:send carries { text }. Header buttons emit imessage:back, imessage:profile, imessage:video; the composer emits imessage:add and imessage:microphone. Keyboard letters, space, delete, and return emit imessage:key with { key }; mode, shift and dictation buttons emit imessage:keyboard-action for the host to implement. All events bubble across Shadow DOM. The React callbacks are onSend, onBack, onProfile, onVideo, onAdd, onMicrophone, onKey, and onKeyboardAction, with (detail, event) arguments.

The composer clears after submission. Applications decide whether to append a bubble or perform any external action. The library never sends real messages, opens contacts, or makes calls.

Exports include createConversation({messages,header,name,avatar,back,video,composer,keyboard,statusBar,theme}), register(), ready(), and the pure bubblePath(width,height,{tail,radius}) helper. A message record has text, optional direction, status, tail, group, and width. The factory uses textContent and never interprets message HTML. Call bubble.refresh() after a CSS-only radius change or await ready() before a browser screenshot.

Fidelity

Measured SVG bubble geometry follows the rendered text. Original screenshot references, comparison fixtures, and overlays live in the repository/demo, outside this npm package. Apple fonts are used through the system stack and are not redistributed. The non-Apple fallback has a small optical size adjustment; native Apple rendering, emoji, antialiasing, and glass effects can differ. Chrome rendering and SSR imports were checked; Safari and Firefox are not independently verified. This is not a pixel-identical reproduction on every platform.

Code: MIT. Independent project, not affiliated with or endorsed by Apple.

Complete configuration

Use individual HTML elements, the React wrappers, or the text-only factory. Boolean shortcuts remain supported; option objects expose the full controls.

import { createConversation } from './index.js';

const conversation = createConversation({
  theme: 'dark',
  scrollable: true,
  height: 956,
  statusBar: {
    time: '10:24', clock: true, island: false,
    signal: true, wifi: true, battery: true,
  },
  header: { name: 'Alex', photo: true, back: true, unread: '12', video: true },
  timestamp: 'Today 10:24 AM',
  messages: [
    { direction: 'received', text: 'How much do we owe you?' },
    { direction: 'sent', text: '$17.64 each!', status: 'Delivered' },
    { direction: 'sent', text: 'Just sent the split.',
      timestamp: 'Today 10:25 AM', status: 'Read 10:26 AM' },
  ],
  composer: { plus: true, microphone: false, placeholder: 'iMessage' },
  keyboard: true,
});
document.body.append(conversation);

scrollable: false creates a content-height composition. scrollable: true fixes the height and scrolls only the messages; status bar, header, composer, and keyboard remain in place. Use a height that leaves space for the enabled components (956px is the keyboard preset). Call conversation.scrollToEnd() when needed; slotted-message additions automatically scroll to the end.

HTML has the same switches: <imessage-conversation scrollable="true" style="--im-height:956px">, <imessage-status-bar time="10:24" clock="false" signal="true" wifi="false" battery="true" island="false">, and <imessage-header photo="false" unread="12">. Empty unread hides the count. photo="false" removes the avatar and compacts the header. Omit a component to remove it entirely.

Add <imessage-timestamp> anywhere in the stack. A bubble's status is a plain-text label: omit it, use Delivered, use Read 10:26 AM, or supply custom text. All text remains local; no messages are sent through Apple services.

Accurate device sizing — recommended integration

The library is calibrated in a 440 × 956 logical CSS-pixel coordinate system, matching the supplied full-device reference's aspect ratio (1320 × 2868 at 3×). This is a reference profile, not a claim that every iPhone uses these dimensions.

Use <imessage-viewport> for a screenshot-like device composition. It lays out content at its logical width and height, then scales the whole canvas uniformly to the available width. At a displayed width of 330px, everything scales by 0.75: text, bubble padding, tails, header, icons, and composer. The resulting height is 717px. Text does not rewrap just because the surrounding landing-page column gets narrower.

<script type="module" src="./imessage/index.js"></script>
<div style="max-width:380px">
  <imessage-viewport width="440" height="956">
    <imessage-conversation scrollable="true" style="--im-height:956px">
      <imessage-status-bar slot="status-bar" time="9:41"></imessage-status-bar>
      <imessage-header slot="header" name="Alex" avatar="./alex.png"></imessage-header>
      <imessage-timestamp>Today 9:41 AM</imessage-timestamp>
      <imessage-bubble direction="received">How much do we owe you?</imessage-bubble>
      <imessage-bubble direction="sent" status="Read 9:41 AM">$17.64 each!</imessage-bubble>
      <imessage-composer slot="composer"></imessage-composer>
    </imessage-conversation>
  </imessage-viewport>
</div>

React exports Viewport with width and height props; use the same nesting. For a flat message-only crop, choose a logical height that fits the messages and omit scrollable; continue to scale uniformly. The editor already renders its previews at a fixed 440px logical width before scaling.

Preserve the reference geometry

  • Keep default 17px text, 20px line height, 20px horizontal conversation insets, 10px message gaps, and 78% maximum bubble width. Internal fallback-font compensation is already included; do not apply another correction.
  • Keep the 58px status bar, 100px profile header with 60px portrait, 44px navigation controls, and native composer. Do not replace components with approximately styled copies.
  • Use one scale factor on the entire canvas. Do not shrink only the text, tighten only the margins, or stretch width and height independently to fit a card. Do not combine percentage-width layout with independent font-size breakpoints.
  • Surrounding page styles can override custom-element host styles. CSS resets such as * { margin: 0 } must be scoped away from conversation descendants, or restore bubble margins explicitly: imessage-bubble { margin:0 auto 10px 0 } imessage-bubble[direction="sent"] { margin-left:auto; margin-right:0 }. Shadow DOM protects internal styles, not host styles.
  • Use an iframe with a minimal stylesheet when embedding into an unknown CSS environment. This is how the editor isolates exported compositions.
  • Treat decorative bezels as outside the screenshot canvas. Do not subtract a thick border from the logical viewport or invent an arbitrary phone aspect ratio.
  • For another device profile, set its actual logical dimensions and recalibrate against that device's screenshot; changing viewport width can legitimately change line wrapping.

Check the result

Use the same text and logical dimensions as the reference. Compare at the same displayed scale, checking line breaks, received/sent alignment, bubble tails, header size, and composer placement. Use the editor's message overlay for bubble geometry. Check desktop and mobile displays without modifying the internal layout. Test on Safari/iOS for final font and emoji fidelity; Linux fallback rendering and approximate Liquid Glass cannot be claimed as pixel-identical to iOS.

All example portraits must be fictional or explicitly supplied for public reuse. The current demo uses a generated fictional portrait; personal profile screenshots are excluded from the current downloadable source.

Message arrival animations

Add entrance to newly inserted bubbles for a short upward glide and expansion, with direction-aware origins, smooth space allocation, and a delayed receipt fade. Existing messages stay still. Motion respects prefers-reduced-motion and preserves the final device sizing.

<imessage-bubble direction="sent" entrance>On my way!</imessage-bubble>

In React use <Bubble direction="sent" entrance>On my way!</Bubble>. Append messages with stable keys so previous bubbles are not remounted. Call bubble.reveal() to replay an entry explicitly. Omit entrance for static compositions and screenshot comparisons.