agentleFS
robritacca-dotcomrobritacca-dotcom/rift-ds/.claude/skills/new-template/SKILL.md

new-template

Skill9 starsChanged 7 days agoMITView on GitHub

Build a new template screen — a complete product screen composed from the design system alone — and wire it into every surface that tracks templates. Use when asked to add, create, or build a template screen.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill when asked to add, create, or build a template screen — phrases like "add a [X] template", "build a template screen for …

    line 15
  2. Distinctive:

    All demo data is fictional — an invented product with an invented name, never a real company's screen with the serial numbers filed off.

    line 25
  3. Very specific:

    Create website/src/components/templates/<Name>/ with <Name>.tsx + <Name>.module.css:

    line 44
  4. Very specific:

    Create website/src/app/templates/<slug>/ with two files, mirroring a live template's:

    line 57

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

new-template

Build a new template screen — a complete product screen composed from the design system alone — and register it everywhere the site tracks templates.

When invoked

Use this skill when asked to add, create, or build a template screen — phrases like "add a [X] template", "build a template screen for [X]", "the templates need a [X]".

A template is a website surface, not a library component: it lives in website/src, imports the published package, and gets no design.md component spec, no Storybook story, and no registry entry in src/components/registry.json. If the work is really a new component the screen needs, do that first with the new-component skill.

Instructions

1. Read the conventions before composing anything

  • design.md's Template screens section owns the family's composition rules — read it in full; it exists so the next screen lands right without re-learning the review rounds that produced it.
  • design.md's Composition section owns the page-assembly rules the family sits on (parent owns spacing, one level of chrome, a component that brings its own bordered chrome sits directly on the page).
  • All demo data is fictional — an invented product with an invented name, never a real company's screen with the serial numbers filed off. content-design.md's template-screens register row owns the copy rules.

2. Read the exemplars

The live implementations under website/src/components/templates/ are the source of truth for structure — pick the one nearest the new screen's shape and mirror it rather than improvising:

  • A data view (table or board as the stage), or a dashboard of KPI tiles and charts over one — merchant sales, which is also the exemplar of a live assistant and of the entity switcher
  • A timeline or scheduling stage — the roadmap planner or team calendar
  • A stage behind a scrolling grid (a map under the first screen, no KPI band, a Describe interpreter that bills no model) — the analytics template
  • An assistant home (the real chat as the page, a board of tiles under the composer) — the chat home
  • An instrument with one subject in two projections (a map or globe stage, a stage-mounted toolbar, full-width content) — the relay console
  • A phone-only app screen (platform chrome, drawn as a framed device on the dotted stage at wider widths) — the mobile dashboard
  • A single centred form with no app shell — the sign-in screen
  • A marketing page with no app shell (a hero, sections and a footer, a hamburger drawer at narrow widths and a bottom tab bar on a phone) — the landing page

Each implementation's doc comment records its own composition decisions; read the chosen exemplar's before writing.

3. Build the implementation

Create website/src/components/templates/<Name>/ with <Name>.tsx + <Name>.module.css:

  • Open the .tsx with a doc comment in the exemplars' shape: what the screen is, the fictional product, the composition calls made and which design.md rules they follow, and the corpus-exclusion pointer.
  • A screen with a sidebar lays out through TemplateShell (website/src/components/templates/TemplateShell/): pass sidebar (the rail's own props, without layout, sub-nav mode, expanded state or footer), chatOpen, frame, tabBar, assistant, contentClassName and, for an instrument or board whose panes scroll themselves, fill="viewport". The shell owns the rail and its footer switchers, the app shell mode the visitor picks, flush, floating or inset (stored by website/src/lib/templateLayout.ts; a template has no backdrop, which is a chat's alone), viewport sizing, the docked assistant's width reservation and the Mobile-preview swap; the template styles only its content column (its padding, gap and floating max width). The shell runs the rail in panel mode with the side panel closed, so give two or three nav items demo children. Wrap the top bar (search, then AiButton and the notifications CircularButton, both at the default size) in StickyTopBar from the shell's folder, with no Divider under it. design.md's App shells section owns the three modes and its Template screens section these conventions.
  • A screen without a sidebar sizes its shell from --layout-viewport-height (height: or min-height: calc(var(--layout-viewport-height, 100vh)), whichever the screen's flow needs), never bare 100vh: the templates index carousel renders pages in scaled same-origin iframes and pins that variable to give viewport-tall shells a fixed size.
  • The assistant panel, three shapes, the mock the default: a screen that wants a mock chat uses the shared TemplateAssistant (website/src/components/templates/TemplateAssistant/), which owns the site chat's responsive modes (docked at the dock threshold, a modal overlay below it, a takeover at phone widths; design.md's Template screens rule 7). Inside TemplateShell the host passes chatOpen and the shell reserves the width, and the assistant reads the shell's layout (docked flat at the shell gap in the inset layout, with the site chat's widen grip); a screen outside it gates its own reservation on @media (min-width: 1440px); a host that seats the panel inside a frame of its own passes placement="contained" and places it through className (the mobile dashboard hosts it as a full-screen sheet inside its phone; that prop is the assistant's own and unrelated to the site chat's Chat position menu, which a template does not offer — leave placementEnabled off on a hosted ChatWidget, design.md's ChatWidget spec owns the rule); and a screen whose subject is the chat hosts the package's real ChatWidget (rift-ds/chat) on the scripted demo transport (createDemoTransport() with DEMO_RENDERERS from rift-ds/chat/demo), inside its own SiteChatProvider (the chat home). Reach for the second only when the widget's own behaviour is the thing being shown — it brings the live component's state with it, so the screen has to own the open/closed handling too, and it leaves context unset so the staged assistant does not announce this site's page name. When the chat is the page itself rather than a panel beside one (the chat home), seat it as the whole viewport: closeEnabled={false} and fullscreenEnabled={false}, setOpen(true) on mount, phone and compact from useTakeoverViewport(), the built-in history rail with threads (seeded through the provider's threads option), and the screen's board in the widgets slot (design.md's ChatWidget spec owns the slot and the exception). The third shape, a live assistant over the screen's own data, passes a real ChatWidget as TemplateAssistant's chat (design.md's Template screens rule 7, with merchant sales the reference); it bills a model, so reach for it only when asked. One that follows what the reader picks or proposes changes they confirm builds on the library's primitives rather than its own (useSelectionFocus with describeSelection for what a highlight sits in, useGeneratedStarters, defineAction from rift-ds/chat/actions, ActionCard and ActionReadout; design.md's ChatWidget patterns own the flow), and gets an eval beside evals/merchant-chat.
  • A sidebar shell previews as an app in a Mobile preview (the carousel's Mobile mode and the playground's Mobile stage both set the flag; design.md's Template screens rule 8 owns it). Pass frame (the large-title bar's title and subtitle) and tabBar (an AppFrameTabBar from the same nav sections, with the assistant on its circle) to TemplateShell, which does the swap; inside the content, read useAppFrame() from website/src/components/templates/AppFrame/ to skip the web top bar and the page-title block. Merchant sales is the reference wiring. A screen with no tab-bar shape on a real phone (a chat session, a sign-in) skips this.
  • Responsive steps come from the breakpoint modules: the template family's BREAKPOINT_TEMPLATE_COMPACT_PX / BREAKPOINT_TEMPLATE_NARROW_PX in website/src/config/breakpoints.ts, the chat dock and takeover and the layout set in src/tokens/breakpoints.ts. A screen whose own grid breaks somewhere else says why with a ds-allow(breakpoint) directive above the query (scripts/validate-breakpoints.mjs enforces it)
  • Every control is a library component, every value a semantic token — the standard component and token rules apply unchanged. The product's name wears the shared ProductMark (website/src/components/templates/ProductMark/), a publisher or identity provider the screen names takes its mark from PublisherMarks (website/src/components/templates/PublisherMarks/), and demo humans are Avatar initials; design.md's Template screens section owns all three.
  • Dates and times in demo data are pinned strings formatted by hand, so the statically built HTML and the hydrating client can never disagree over a locale or a clock (the exemplars' convention).

4. Create the route

Create website/src/app/templates/<slug>/ with two files, mirroring a live template's:

  • page.tsx — a doc comment (why the route is chromeless and corpus-excluded, pointing at both lists) and a default export that renders the implementation. Nothing else.
  • layout.tsx — export const metadata = pageMetadata("/templates/<slug>", "<description>") (import from @/config/navigation). The description is real page metadata, so it follows content-design.md like any shipped copy.

5. Register the screen — three lists and the playground stills, in one change (a fourth list when the screen has a grip)

  • templatesSidebarLinks in website/src/config/navigation.ts — add { href, label, description }, plus mobileOnly: true for a screen with no tablet or desktop layout (the carousel then keeps its slide in the phone frame at every device size; the field's doc on NavLink owns the contract), and hideFromShowcase: true only if the screen should stay out of the index carousel while keeping its page, sidebar entry and sitemap row: the index link stays first, then one entry per screen in the family's curated order. This is the one authoritative list of templates: the index carousel, the sidebar, the sitemap, llms.txt, the chat corpus's Templates list, and the playground's Templates rail all derive from it. No validator holds it, so a skipped entry fails silently — the screen simply never appears anywhere.
  • CHROMELESS_ROUTES in website/src/config/chromeless.ts — the shared footer, chat panel, and palette would otherwise render inside the app shell being shown. Build-enforced indirectly: validate-page-summaries.mjs reads this list as its coverage exemption, so a template route left out fails the build demanding a page summary the screen should not have.
  • EXCLUDED_ROUTES in scripts/generate-site-corpus.mjs — with a written reason in the existing entries' shape (fictional demo data; the screen's name and summary reach the corpus through the site map's Templates list, drawn from the sidebar entry). This one is a gate: validate-chat-coverage.mjs fails the build for an uncovered route.
  • The playground's template stills, unless the screen is mobileOnly: run node scripts/capture-template-thumbnails.mjs and look at the two new pictures before committing them. The script serves the built site, and for a new template the website's npm run build cannot finish yet: its prebuild runs validate-template-thumbnails, which fails until the stills exist. Build with npx next build inside website/ for the capture (the prebuild chain is what you are skipping, so run it afterwards). Passing a running dev server as the origin instead puts Next's dev indicator into every still. The script shoots every listed template, so restore the unchanged ones (git checkout -- website/public/images/templates/, which leaves the new, untracked pair alone) and commit only the stills that should change. This one is a gate: validate-template-thumbnails.mjs fails the build for a listed template with no still in either mode, but only for a missing file: a still also goes stale when an existing screen changes what it shows, so re-run the script then too (CLAUDE.md's Screenshot surfaces entry owns the rule).
  • scripts/gestures.json, only when the screen carries a grip a visitor can drag (a resizable pane, a docked rail's widen grip): one entry per grip with the setup that reaches it, so the gesture stress check drives it in verify and CI (CLAUDE.md's Gestures row owns the registry). No validator notices a missing entry.

Nothing else needs registering, unless the screen has a live assistant, which also needs its own route behind the shared guardrails and a sentence on /privacy. Chromeless routes are automatically exempt from the AI-summary panel (validate-page-summaries.mjs) and the anchor rail; breadcrumbs and page title derive from the sidebar entry.

6. Verify

Load the route in the browser and confirm: the shell fills the viewport with no shared chrome inside it, both themes render, and the templates index carousel picked the screen up (it derives from the sidebar entry — an empty slide or a missing one means step 5 went wrong). For a mobileOnly screen, also check the route at a phone width (the app edge to edge) and at 600px or wider (the framed device), and that its carousel slide stays in the phone frame with the device toggle on Desktop and Tablet. For a sidebar shell, switch the carousel to Mobile and confirm the slide shows the large-title bar and the tab bar, and that the route itself at a phone width still shows its own narrow layout. Then run the corpus generator or npm run validate-registry to prove the exclusion entry satisfies the coverage gate.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.