Use when changing Hussh UI architecture, shared components, shell chrome, or styling rules inside the frontend owner family.
Resources
2Install
npx skillscat add hushh-labs/hushh-research/frontend-design-system Install via the SkillsCat registry.
SKILL.md
Hussh Frontend Design System Skill
Purpose and Trigger
- Primary scope:
frontend-design-system - Trigger on shared UI architecture, reusable surface primitives, shell chrome, styling rules, and design-system policy changes.
- Avoid overlap with
frontend-architectureandfrontend-surface-placement.
Coverage and Ownership
- Role:
spoke - Owner family:
frontend
Owned repo surfaces:
hushh-webapp/components/uihushh-webapp/lib/morphy-uxhushh-webapp/components/app-uidocs/reference/quality/design-system.md
Non-owned surfaces:
frontendmobile-nativedocs-governance
Do Use
- Shared component, shell chrome, Morphy UX, and app-ui work.
- Design-system rules that require docs or verification updates.
- Reusable visual, layout, interaction, form, and copy primitives.
Do Not Use
- Broad frontend intake where the correct spoke is unclear.
- Native plugin or mobile parity work.
- Route-contract and package-convention work without a design-system rule change.
Read First
docs/reference/quality/design.mddocs/reference/quality/design-system.mddocs/reference/quality/frontend-ui-architecture-map.mddocs/reference/quality/app-surface-design-system.mddocs/reference/quality/frontend-pattern-catalog.md.codex/skills/frontend-design-system/references/design-review-kernel.mddocs/reference/mobile/render-performance-charter.md
Workflow
- Read design-system and frontend architecture docs before touching shared UI.
- Decide the owning layer first: stock UI, Morphy UX, or app-ui.
- Keep route-container ownership with shared shells.
- Update docs or verification commands when the design rule itself changes.
- Keep persona-facing labels plain-language and route action ids aligned to One/Kai/Nav ownership. For One consent/vault copy, frame user-held knowledge/information as safewords and avoid generic
your informationonboarding language. Onboarding is One-first with downstream sub-onboardings (Kai/RIA/KYC): model every flow via the registry inhushh-webapp/lib/navigation/onboarding-registry.ts(One is the only account-scoped gate, reappearing only on reset/delete; subs are surface-scoped, independently re-enterable), gate on the authoritative store not the deprecatedkai_onboarding_requiredcookie, and await server completion sync before navigating. Seedocs/reference/quality/one-onboarding-architecture.md. Follow Shell and navigation ownership indocs/reference/quality/app-surface-design-system.md: astandardroute declares a breadcrumb, which is what mints its back control. - Keep consumer notifications, Email Helper rows, and background-task rows free of implementation diagnostics. Do not show
PKM, manifests, schemas, tokens, thread ids, workflow ids, consent ids, hashes, timings, correlation ids, route names, raw errors, or dummy-save language outside developer-only surfaces. - For signed-in route shell, header, search/filter, or hover changes, verify shared
AppPageShell,PageHeader,KaiCommandBarGlobal, and app-ui interaction contracts before adding route-local chrome. - Use shadcn Sonner for transient success/error/loading feedback and shadcn AlertDialog for destructive confirmation. Do not add route-local inline error banners for row actions, saves, deletes, refreshes, or short-lived failures; reserve inline errors for stable page-blocking states. For async actionables that wait for a backend ack/status (deletes, resets, disconnects, sends), use the branded
morphyToast.promisefrom@/lib/morphy-ux/morphytied to the real action promise so one toast morphs loading -> success/error in place; never pair a manual loading toast with a separate success/error toast or fire success before the promise resolves. Confirmation buttons (AlertDialogAction/AlertDialogCancel) carry the shared Material ripple fromcomponents/ui/alert-dialog.tsx; do not swap them to the plain@/components/ui/buttonor strip the ripple host. See the Consumer Copy Contract and Ripple Ownership and Clipping sections inapp-surface-design-system.md. - When changing approval-gated Email Helper drafts or preview templates, use the canonical
agent_kyc.approved_disclosure_formatter.v1client renderer inhushh-webapp/lib/services/one-kyc-approved-disclosure-renderer.tsthrough the strict-ZK service. Do not hand-roll route-local email HTML, and keep plaintext/HTML output derived from the same render model. - Gmail-safe dense information tables must live in an inline-styled horizontal scroll wrapper with a fixed/minimum table width. Do not squeeze holdings or financial tables into mobile width if that causes overlap, truncation, or unreadable numeric columns.
- Signed-in Email Helper, PKM, and consent UI proof must use the reviewer-mode vault-owner identity resolved from runtime env. Do not treat counterparty labels, copied recipients, thread ids, or admin fixtures as the active information owner.
- Profile sharing controls must present the three visibility postures as plain language:
Private,Ask first, andAvailable by default. Do not showvisibility_posture,default_available,scope,manifest,registry, ornon-consented exportin consumer copy. - Review composition, hierarchy, responsive layout, interaction, form geometry, copy, contrast, and accessible semantics through
design-review-kernel.md. Apply its readability, predictability, cohesion, and coupling lenses to the implementation; challenge asymmetric or noisy UI before shipping. - Form controls have an explicit shared contract:
Input,InputGroup,Textarea,SelectTrigger,CommandInput, and combobox field shells use--app-input-radius; compound controls keep inner inputs square. Use--app-form-field-gap,--app-form-related-gap, and--app-form-section-gapfor label, related-action, and section rhythm. A secondary recovery link may be visually small but must retain a transparent 44px hit area. Credential fallback groups keep the “Can’t get in?” descriptor on one centered line and all available quiet actions on the next centered row inside a capped form measure; separate multiple actions with middle-dot separators and use no second primary CTA, pill, column, or vertical divider. If only one action is available, keep it as the single centered element without a separator. Use--app-form-section-gapfor the larger pause after the primary unlock action and--app-form-related-gapto keep the descriptor close to its action row. Keep visible text close to the descriptor and avoid pill padding,self-startoffsets, or left-anchored rows. Recovery, alternate unlock, and Sign out share the same quiet escape row when present. - Match all standalone buttons, pill triggers, and icon controls to the gold-standard flat-control recipe owned by the agent bar, bottom nav, and top-app-bar buttons. Reuse
ShellActionSurface(SHELL_ICON_BUTTON_CLASSNAME/SHELL_PILL_TRIGGER_CLASSNAME) rather than re-derivingrounded-full+bg-black/[0.05] dark:bg-white/[0.07]+ flat hover/press per surface. Sibling controls in one group must share onemorphy-uxeffect (do not mixglassandfade). See the Control Surface Contract inapp-surface-design-system.md. - Enforce the Pixel Grid And Symmetry Contract in
app-surface-design-system.md: shared start lines and fixed launcher tracks, centering a finite roster but left-anchoring a larger collection. Route headers use the lean sharedPageHeader;SettingsGroup/SettingsRowown responsive insets. Never stretch a few icons across a wide card or duplicate route chrome. - Give every modal floating surface the shared backdrop thump. Dialogs/sheets/drawers/command palette/vault dialog inherit it from
DialogOverlay; modal popovers opt in withPopoverContent withBackdrop(rendersdata-slot="popover-scrim"animated by the sharedoverlay-scrim-*keyframes). Do not hand-roll per-surface scrim opacity, blur, or duration. Bottom navigation is the five-item signed-in sequenceChat,One,Connect,Feed,Search, withChatrooted at/; Search remains a segment and Finance/RIA contextual tabs are route-driven inside the unified top shell, never route-local or bottom-navigation tabs. Profile is the rightmost signed-in top-bar control with the shared image/generic fallback. The voice control is a narrower slot in the same bottom surface, with no divider, nested material, or chat-overlay launcher. There is exactly ONE app-wide route transition (uniform exit->enter crossfade):useRouteTransition(mounted once inapp/providers.tsx) intercepts<a>clicks AND patches the History API once, so everyrouter.push/router.replaceinherits the crossfade with zero per-site code — never add a parallel navigation animation (framer-motiontemplate.tsx, View Transitions, per-route motion); a hard-cut screen bypassedrouter/<a>, so route it throughrouter, and keepEXIT_MS/ENTER_MSinlib/morphy-ux/hooks/use-route-transition.tssynced with--motion-route-exit/enter-duration. See the Overlay Backdrop Contract and Bottom Navigation Contract inapp-surface-design-system.mdand the Route transitions section inlib/morphy-ux/README.md. - Agent Chat and portfolio import streaming surfaces use
components/app-ui/stream-progress-panel.tsxas the shared primitive. Active assistant stream panels must be full-width, progress/thinking/response sections must stay distinct, marketplace opportunity accordions must be preloaded by the workspace when possible, and mobile chat history drawers must use shared glass chrome rather than flat white desktop panes. See the Agent Chat Stream Surface Contract inapp-surface-design-system.md. - In-page step/tab enters use the shared
.motion-step-enterutility (app/globals.css, motion tokens, reduced-motion safe) - never ad-hocanimate-in fade-in slide-in-* duration-N. Dark chrome tints (bar glass, chrome-glass-surface, morphy-app-bg, route palettes like--one-bg/--one-card) derive fromcolor-mix(in oklab, var(--background) N%, transparent), never hardcodedrgb(28,28,30)/#1c1c1e(lighter than true--background= milky band / "double card" partition). Visualizations are honest: sparklines only from real series scaled to their own min/max, degraded rows get aDelayedchip + flat dashed baseline, never invented always-green paths. Drawer flows (vault unlock) keep ONE surface: no opaque card insideDrawerContent(morphyCard effect="fill"needs!bg-transparent). - Frame pacing is a contract, not an audit: follow
references/render-performance-kernel.md(transform/opacity only, no per-frame<html>writes, no non-passivewindowtouch listeners, no body-wide observers, no React state per frame,CHART_ANIMATION_ACTIVE, the--z-*ladder) and ship shell/sheet/streaming changes with probe numbers. - Initial source/import decisions are short task selectors, not dashboards: use
PageHeaderplus oneSettingsGroup/SettingsRowlist at the reading measure. The first mobile viewport must contain the choices and deferral action; do not add badge clusters, drag-and-drop panels, duplicate CTA buttons, or a setup-terminal action until the user completes or defers a source choice. - Ambient shell material has one owner:
AmbientChromeMaskandlib/morphy-ux/ambient-chrome.ts; both edges render on desktop/mobile with no route gradients or second engine. The top mask spans resolved shell height, stays solid through thebar-with-tabsunderline, and stops there with a hard edge: the top has no fade band or gradient tail. Use the route-layout body-offset contract for additional reading space; never add a second mask or route-local gradient. - Finance is one query-tabbed One workspace: Profile's
AppPageShell width="reading"owns the gutter and the top shell owns tabs; render one primaryPageHeader, never route-local tabs, second fixed headers, or a wider dashboard canvas.
Handoff Rules
- Broad or ambiguous frontend work routes back to
frontend. - Route contracts or verification ownership route to
frontend-architecture. - File placement or layer ownership routes to
frontend-surface-placement. - Cross-domain scans start with
repo-context.
Required Checks
cd hushh-webapp && npm run verify:design-system
cd hushh-webapp && npm run verify:render-performance
cd hushh-webapp && npm run verify:cache
cd hushh-webapp && npm run verify:docs
cd hushh-webapp && npm run typecheck