Guidelines
Web design guidelines
Guidelines v1.1.0UI, accessibility, and chrome rules for Shellui website, docs, and in-shell apps.
For agents
Load skills/web-design-guidelines/SKILL.md, then read this file - not the HTML. Same source as this page.
content/guidelines/web-design.md https://shellui.com/guidelines/web-design.md
These rules cover layout, interaction, and visual language on shellui.com, docs.shellui.com, and apps hosted in the Shellui iframe. Pair them with the writing guidelines for copy. Lead with the shipping shell: navigation, authentication, administration, storage, light and dark themes. Keep AI, MCP, and marketplace chrome out of the default UI.
Agents: load this file. Do not scrape the HTML page. The version field in the frontmatter is the public "Guidelines v…" label.
How to use this file
- Humans: read the page at
/guidelines/web-design/, or download this markdown - Agents: read
content/guidelines/web-design.mdin this repo, or fetchhttps://shellui.com/guidelines/web-design.md - Skill wrapper:
skills/web-design-guidelines/SKILL.md - Sibling: writing guidelines for voice and tone
- Compose pages with: design.md (
fetch https://shellui.com/design.md) - Hub: Guidelines
Review files against every rule below. Output concise, high-signal findings. Sacrifice grammar for brevity.
Shellui chrome
The product is a microfrontend shell. The host owns shared chrome; the iframe owns the app.
- Host chrome: sidebar, top bar, or window layout, plus toasts, dialogs, drawers, theme, and language. Do not rebuild those inside the iframe
- Iframe apps talk to the shell with
@shellui/sdk(init, postMessage). Do not reach into host DOM - Layout mode is a shell concern (
shellui.config.ts). App UI must work in sidebar, top-bar, and window layouts - Do not cover the shell header, nav, or toast region. Fixed app bars need an offset for host chrome
- Authentication, admin (
/admin), and Files are shell surfaces. Deep-link them; don't clone their navigation - Theme and locale live in the shell. Read them; don't ship a second theme picker in the app unless the app is the settings surface
- Prefer existing Tailwind tokens. Primary accent is honey-gold (
--color-primary,--color-primary-ink). Don't invent a second brand color - Marketing site chrome: fixed header (
h-16),page-toppadding, footer. Sticky in-page UI must clear the header - Logos and marks: download from Brand assets. Don't stretch, recolor, or swap in a different wordmark
Accessibility and focus
- Icon-only buttons need
aria-label - Form controls need a
<label>oraria-label - Interactive elements need keyboard handlers (
onKeyDown/onKeyUpwhere click is not enough) <button>for actions;<a>for navigation. Never a<div>with a click handler- Images need
alt(oralt=""if decorative) - Decorative icons need
aria-hidden="true" - Async updates (toasts, validation) need
aria-live="polite". Shell toasts already live in the host - use the SDK, don't mount a second live region that fights the host - Semantic HTML (
header,nav,main,footer) before ARIA - Headings hierarchical
h1–h6; oneh1per page - Include a skip link to main content on long chrome (shell and marketing header)
scroll-margin-topon heading anchors so the fixed header does not cover the target- Meaningful media needs captions, transcripts, or descriptions
- Media controls need keyboard support; hide decorative media from assistive tech
- Interactive elements need visible focus:
focus-visible:ring-*or equivalent - Never
outline-nonewithout a focus replacement - Use
:focus-visibleover:focus(avoid a ring on pointer click) - Group focus with
:focus-withinfor compound controls - Trap focus in dialogs; return it to the trigger on close
- Hit targets: if the visual control is < 24px, expand the hit area to ≥ 24px; on mobile ≥ 44px
- Status is not color alone; include a text label
- Sticky headers, footers, and overlays must not cover the focused element (16 × 4px marketing header; shell top bar in top-bar layout)
Forms and input
- Inputs need
autocompleteand a meaningfulname - Use the correct
type(email,tel,url,number) andinputmode - Never block paste (
onPaste+preventDefault) - Labels clickable (
htmlForor wrapping the control) - Disable spellcheck on emails, codes, usernames, tokens (
spellCheck={false}) - Checkboxes and radios: label and control share one hit target (no dead zones)
- Submit stays enabled until the request starts; spinner during the request; keep the original label
- Do not pre-disable submit on incomplete forms; submitting surfaces errors
- Do not block keystrokes on typed fields; validate after input
- Enter submits when a text input is the only control; in
<textarea>, ⌘/Ctrl+Enter submits - Errors inline next to fields; focus the first error on submit
- Placeholders end with
…and show an example pattern (your_access_token_here…) autocomplete="off"on non-auth fields to avoid password-manager triggers- Warn before navigation with unsaved changes (
beforeunloador a router guard) - Auth fields belong in the shell when the app is hosted. Don't duplicate sign-in UI in the iframe unless you are replacing the identity backend
Motion, touch, and layout
- Honor
prefers-reduced-motion(reduced variant or disable). The marketing theme reveal already disables under that media query - match it - Animate
transformandopacityonly (compositor-friendly) - Never
transition: all; list properties explicitly - Set the correct
transform-origin - SVG: transforms on a
<g>wrapper withtransform-box: fill-box; transform-origin: center - Animations interruptible - respond to input mid-animation
- Autoplay motion longer than 5 seconds needs pause, stop, or hide controls
- Muted decorative loops must stop under
prefers-reduced-motion touch-action: manipulation(prevents double-tap zoom delay)- Set
-webkit-tap-highlight-colorintentionally overscroll-behavior: containin modals, drawers, and sheets - including shell drawers- During drag: disable text selection,
inerton dragged elements - Drag, swipe, pinch, and path gestures need tap/click and keyboard alternatives unless the gesture is the whole point
autoFocussparingly - desktop only, single primary input; avoid on mobile- Full-bleed layouts need
env(safe-area-inset-*)for notches - Avoid unwanted scrollbars: fix overflow rather than masking it with
overflow-x-hiddenonbody - Flex and grid over JavaScript measurement for layout
- Nested radii: child ≤ parent, concentric (
rounded-mdinsiderounded-xl) - Design empty, sparse, dense, and error states
- Inline help before tooltips
- Iframe apps: don't assume viewport height equals the window. The shell chrome eats space. Prefer
%/ flex inside the iframe, not100vh, unless you subtract host chrome
Visual language
- Density: developer-facing, not crowded. Match existing
page-top,max-w-5xl/max-w-3xlprose,rounded-xlcards, and gray borders already on brand-assets and feature pages - Light and dark are both first-class. Preview every screen in both. Marketing site:
html.darkpluslocalStorage.theme. Product:@shellui/coretheme color-scheme: darkonhtmlwhen the dark theme is active (fixes scrollbar and native controls)theme-colormeta matches the page background- Native
<select>: explicitbackground-colorandcolor(Windows dark mode) - Ellipsis
…, not three dots.... Loading states:Loading…,Saving… - Prefer hyphen
-for a pause in UI copy. Ban em dashes (—) and en dashes (–) used as punctuation. Ordinary hyphens stay - Straight quotes in source. Don't convert to curly quotes by hand
font-variant-numeric: tabular-numsfor number columns and comparisonstext-wrap: balanceortext-prettyon headings (prevents widows)- Text containers handle long content:
truncate,line-clamp-*, orbreak-words - Flex children need
min-w-0to allow truncation - Handle empty states - don't render broken UI for empty strings or arrays
- User-generated content: anticipate short, average, and very long inputs
- Buttons and links need a
hover:state. Hover, active, and focus should increase contrast, not flatten it - Copy in the UI follows the writing guidelines: active voice, sentence-case page headings, Title Case nav labels, specific button verbs ("Save token", not "Continue")
- Numerals for counts: "8 apps", not "eight"
- Error messages include the next step, not only the problem
&over "and" only where space is tight (nav, buttons)- Brand name Shellui never translates:
translate="no"on the wordmark in HTML where auto-translate would garble it
Performance and state
<img>needs explicitwidthandheight(prevents layout shift)- Below-fold images:
loading="lazy" - Above-fold critical images:
fetchpriority="high" - Large lists (more than 50 items): virtualize or
content-visibility: auto - No layout reads in render (
getBoundingClientRect,offsetHeight,offsetWidth,scrollTop) - Batch DOM reads and writes; don't interleave
- Prefer uncontrolled inputs; controlled inputs must be cheap per keystroke
preconnectfor CDN and asset origins you actually use (jsDelivr for@shellui/sdkon this site)- Critical fonts: preload with
font-display: swap. This marketing site uses the system stack - don't add a webfont without a reason - Prefer compressed video over animated GIF; provide a still alternative
- Short decorative loops: muted video,
prefers-reduced-motionmedia condition, still fallback - URL reflects state - filters, tabs, pagination, expanded panels in query params. Deep-link stateful UI
- Links are real links (
<a>), so Cmd/Ctrl+click and middle-click work - Destructive actions need a confirmation modal or an undo window - never immediate
- Dates and times:
Intl.DateTimeFormat, not hardcoded strings - Numbers and currency:
Intl.NumberFormat - Detect language via
Accept-Language/navigator.languages, not IP. The shell already owns locale - Hydration: inputs with
valueneedonChange(ordefaultValueif uncontrolled) - Date/time rendering: guard against server/client mismatch
suppressHydrationWarningonly where it is truly required- Eleventy pages are static HTML. Don't hydrate a whole marketing page for a theme toggle - keep Alpine/JS scoped
Anti-patterns (flag these)
user-scalable=noormaximum-scale=1disabling zoomonPastewithpreventDefaulttransition: alloutline-nonewithout a focus-visible replacement- Click handler navigation without an
<a href> <div>or<span>with click handlers that should be<button>or<a>- Images without dimensions
- Large arrays
.map()without virtualization - Form inputs without labels
- Icon buttons without
aria-label - Submit disabled before the user tries (hides validation)
- Hit targets under 24px (44px on mobile) with no expanded area
- Hardcoded date/number formats (use
Intl.*) autoFocuswithout a clear reason- Animated GIF when compressed video is suitable
- Gesture-only action without tap/click and keyboard alternative
- Em dashes in UI copy; "fixing" ordinary hyphens
- Rebuilding shell toasts, dialogs, or nav inside the iframe
100vhlayouts that sit under host chrome- A second theme picker in a hosted app
- Shipping-product UI that leads with AI, MCP, or marketplace chrome
- Missing dark-theme styles (
dark:or theme tokens) - Focus or headings hidden under the fixed marketing header or shell top bar
Review output
Group by file. Use file:line format (clickable in VS Code). Terse findings.
## src/guidelines/index.njk
src/guidelines/index.njk:12 - icon button missing aria-label
src/guidelines/index.njk:40 - heading target missing scroll-margin-top
src/guidelines/index.njk:58 - animation missing prefers-reduced-motion
## src/assets/css/extras.css
src/assets/css/extras.css:22 - transition: all → list properties
src/assets/css/extras.css:41 - outline-none without focus-visible replacement
## src/_includes/nav.njk
✓ pass
State issue + location. Skip explanation unless the fix is non-obvious. No preamble.