` containing an empty `
` above the toolbar - the editing surface comes first and the toolbar last.
- The toolbar is `p-2 flex flex-col sm:flex-row sm:justify-between sm:items-center gap-2 sm:gap-3 border-t border-line-2`: formatting buttons wrap on mobile and sit opposite the submit button from `sm` up.
- Exactly 9 formatting buttons, all `size-8 rounded-md` ghost buttons with `size-4` outline icons, in this order and with these exact hooks: `data-hs-editor-bold`, `data-hs-editor-italic`, `data-hs-editor-underline`, `data-hs-editor-strike`, `data-hs-editor-link`, `data-hs-editor-ol`, `data-hs-editor-ul`, `data-hs-editor-blockquote`, `data-hs-editor-code`.
- The right side of the toolbar holds one primary button reading exactly `Post` (`py-1.5 px-3 text-sm font-medium rounded-md bg-primary`).
- Below the card, right-aligned, is a `text-xs` muted line with a `size-3` info-circle icon reading `Only you and other staff can see comments`.
- The editor is TipTap v2.11 loaded as ES modules (`@tiptap/core` Editor plus StarterKit with `history: false`, Placeholder, Paragraph, Bold, Underline, Link, BulletList, OrderedList, ListItem, Blockquote). The editable area gets `relative min-h-40 p-3 focus-visible:outline-0`, so the card is at least ~10rem tall when empty.
- The placeholder shown in the empty editor is exactly `Add a message, if you'd like.`, and the link button opens a native `window.prompt('URL')` before applying the link.
- A small TipTap CSS shim ships with the block: `.ProseMirror:focus { outline: none }`, `display: inline` for `p` inside `ul`/`ol`, and the `p.is-editor-empty:first-child::before` rule that renders `data-placeholder`.
Token and Tailwind rules:
- Treat source token classes as semantic roles, not fixed class names to blindly copy.
- Map every source `primary` role to the destination project's current primary token/config/CSS variable. Active links, checked states, progress lines, focus rings, primary gradients, logo primary fills/strokes, and primary buttons must share that destination primary color.
- Do not hardcode Preline blue or emit `text-blue-*`, `bg-blue-*`, `border-blue-*`, `fill-blue-*`, `stroke-blue-*`, `from-blue-*`, `via-blue-*`, `to-blue-*`, or fixed blue hex/rgb values for primary roles unless the destination primary is explicitly blue.
- Before using source-only token classes such as `text-foreground-inverse`, `bg-layer`, `border-layer-line`, `bg-navbar`, `border-line-2`, `bg-dropdown`, or similar Preline tokens, inspect the destination Tailwind config, global CSS/theme variables, and existing components for an equivalent utility/token.
- Prefer existing destination utilities such as `text-white`, `text-primary-foreground`, `text-foreground`, `text-muted-foreground`, `bg-background`, `bg-card`, `bg-popover`, `border-border`, `bg-muted`, `bg-accent`, `ring-ring`, or app-specific tokens when they match the source role.
- If no equivalent exists, add the smallest project-appropriate token or utility before using it. Do not leave undefined source classes in the generated markup.
Asset and icon rules:
- Use exact source images, avatars, product media, branded SVGs, payment marks, channel logos, and app icons when the source provides them.
- Do not replace source assets with initials, emojis, generic icons, generated placeholders, or different stock media.
- Ordinary interface icons should match the destination icon system. If the project uses Lucide-style outline icons, keep the 24 viewBox, `fill="none"`, `stroke="currentColor"`, rounded caps/joins, and source stroke widths.
- Brand/source SVG shape is fixed content. Only map semantic colors such as primary fill/stroke to the destination token.
Behavior and accessibility:
- Preserve source interactions: links, buttons, dropdowns, tooltips, tabs, menus, drawers, modals, toggles, search, filters, form controls, validation states, loading/empty states, and open/closed defaults.
- For source behavior powered by Preline JS or a third-party package, inspect installed dependencies and existing primitives first. Reuse a destination primitive only when it can preserve the source geometry, states, accessibility, and behavior.
- Treat new dependencies as allowed by default unless the user, repository instructions, or package policy explicitly prohibit them. When no faithful equivalent exists, install/use the Preline UI runtime/plugin or the exact source third-party package; do not silently substitute ad hoc JavaScript or custom React state.
- Only under an explicit dependency prohibition may you implement a scoped framework-native equivalent. It must preserve keyboard and focus management, placement and layering, responsive behavior, and accessibility; static or simplified approximations are not acceptable.
- Use semantic HTML and accessible names. Keep `aria-current`, `aria-expanded`, `aria-haspopup`, `role`, labels, focus states, and keyboard behavior when the source exposes them or the destination primitive requires them.
- Desktop and mobile states must not overlap text, clip controls, or shift layout unexpectedly. Controls with icons, counters, badges, and labels should keep stable dimensions across hover/focus/active states.
Final QA:
- Compare the result against the source preview and HTML tab.
- Verify primary color follows the destination project, not Preline's default blue.
- Verify all emitted classes exist in the destination or are added deliberately.
- Verify responsive layout, focus states, dropdown/popover layering, long-text truncation, and source asset paths.
- Keep the final implementation scoped to this block and avoid unrelated refactors.