From a98fea6634c2a14733b79de0a28ebaa0bce5e4f9 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Thu, 10 Sep 2026 12:55:41 -0400 Subject: [PATCH 1/2] first pass on adding new gamut-docs skill --- .../agent-tools/skills/gamut-docs/SKILL.md | 117 ++++++ .../gamut-docs/references/docs-in-code.md | 137 +++++++ .../gamut-docs/references/prose-quality.md | 156 +++++++ .../gamut-docs/references/storybook-docs.md | 380 ++++++++++++++++++ .../Gamut plugin/Best practices.mdx | 1 + .../lib/Meta/Gamut writing guide/About.mdx | 3 + 6 files changed, 794 insertions(+) create mode 100644 packages/gamut/agent-tools/skills/gamut-docs/SKILL.md create mode 100644 packages/gamut/agent-tools/skills/gamut-docs/references/docs-in-code.md create mode 100644 packages/gamut/agent-tools/skills/gamut-docs/references/prose-quality.md create mode 100644 packages/gamut/agent-tools/skills/gamut-docs/references/storybook-docs.md diff --git a/packages/gamut/agent-tools/skills/gamut-docs/SKILL.md b/packages/gamut/agent-tools/skills/gamut-docs/SKILL.md new file mode 100644 index 0000000000..17aae21f8b --- /dev/null +++ b/packages/gamut/agent-tools/skills/gamut-docs/SKILL.md @@ -0,0 +1,117 @@ +--- +name: gamut-docs +description: Use this skill when documenting Gamut — writing or revising a component `.mdx` page, a `.stories.tsx` file, an `About.mdx` index page, prop JSDoc, or code comments. Teaches the Gamut writing guide's structure, voice, and formatting rules plus a prose pass that strips AI-slop patterns; also use it when asked to clean up docs that read as AI-written. +--- + +# Gamut Docs + +How to write and revise Gamut documentation so it is accurate, consistent with the writing guide, and free of generated filler. + +See also: [`gamut-accessibility`](../gamut-accessibility/SKILL.md) — the universal rules to check a component against before writing an `## Accessibility considerations` section, rather than asserting behavior. The component skills (e.g. [`gamut-buttons`](../gamut-buttons/SKILL.md), [`gamut-datatable`](../gamut-datatable/SKILL.md)) carry verified prop and variant detail worth reading before documenting those components. + +Storybook: + +- [Meta / Gamut writing guide](https://gamut.codecademy.com/?path=/docs-meta-gamut-writing-guide-about--page) — the source of every convention in this skill +- [General principles](https://gamut.codecademy.com/?path=/docs-meta-gamut-writing-guide-general-principles--page) · [Stories](https://gamut.codecademy.com/?path=/docs-meta-gamut-writing-guide-stories-about--page) · [Documentation in code](https://gamut.codecademy.com/?path=/docs-meta-gamut-writing-guide-documentation-in-code--page) +- [Atoms / Badge](https://gamut.codecademy.com/?path=/docs-atoms-badge--docs) · [Molecules / Alert](https://gamut.codecademy.com/?path=/docs-molecules-alert--docs) — reference component pages to imitate + +## Reference files + +| Writing | Read | +| ------------------------------------------------------------------- | ----------------------------------------------------- | +| `ComponentName.mdx`, `ComponentName.stories.tsx`, or an `About.mdx` | [`storybook-docs.md`](./references/storybook-docs.md) | +| Prop JSDoc, code comments, or naming in a component file | [`docs-in-code.md`](./references/docs-in-code.md) | +| Any prose at all — always, as the final pass | [`prose-quality.md`](./references/prose-quality.md) | + +Read only what applies. `storybook-docs.md` carries the file layout, the `parameters` object, the section backbone, the `~styleguide/blocks` components, and the story-file conventions. + +## Why this needs a skill + +Gamut docs serve designers and developers at once, and they get read while someone is mid-task and impatient. + +Two qualities make them useful, and they fail independently: + +- **Accuracy** — every prop, variant, story reference, image, and link resolves to something that exists. A doc describing an imagined API is worse than no doc, because readers trust it and build against it. +- **Voice** — plain, concrete, present-tense prose. The failure mode is not bad grammar. It is fluent, confident text that could describe any component in any design system. + +Work in this order: ground in source, draft to the structure, then edit the prose. Drafting first produces confident fiction. + +## Step 1 — Ground every claim in something that exists + +Documentation is derived, not invented. Read the real artifacts first: + +| To write | Read | +| ------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Prop names, types, defaults, variants | The component's props type and implementation in `packages/gamut/src//` | +| `parameters.source.githubLink` | The actual source path; build the URL from it (see [`storybook-docs.md`](./references/storybook-docs.md)) | +| `parameters.status` | The component's real state — do not default to `current` without checking | +| Story references (`of={XStories.Y}`) | The `.stories.tsx` file's actual named exports | +| Anatomy image (``) | `packages/styleguide/src/static//` | +| Internal links (``) | The target story's `parameters.id` or `` — ids are exact strings, not guessable | +| Accessibility considerations | The implementation — verify focus handling, roles, and keyboard behavior rather than asserting them | +| `parameters.design` (Figma URL) | An existing doc for a sibling component, or ask the user | + +Never invent a Figma URL, a prop, a variant name, a story export, or an accessibility guarantee. When a value is not determinable from the repo, write `TODO:` with what is needed and tell the user in your summary. A placeholder costs the team one minute; a plausible-looking fabricated Figma node id or a prop that does not exist costs a debugging session and quietly erodes trust in every other page. + +If the component already has an `.mdx` file, read it before editing. Match its established terms rather than introducing synonyms. + +## Step 2 — Apply the house rules + +These come from the Gamut writing guide and hold across every kind of documentation, so they are worth keeping in mind while drafting rather than fixing afterward. + +**Voice** + +- Active voice, present tense: "The component renders a button", not "A button is rendered by the component" or "will render". +- Imperative mood for instructions: "Add the component", not "You should add the component". +- Active voice and imperative mood usually remove the need for a pronoun. When one is needed, prefer "we"; "you" is fine where it reads naturally ("Use your best judgement"). Never "I", "my", or "me". +- Friendly and conversational, encouraging without overpromising. +- Write for a global audience: no idioms, slang, or culturally specific references. +- Define a term on first use, and then use that same term everywhere — including across the heading, the body copy, and the code sample. One term per concept, and never the same term for two concepts. + +**Code and component references** + +- Backticks for anything that appears in code: `onClick`, `Box`, `variant`, `16px`, `true`, `null`, `Button.tsx`, `@codecademy/gamut`, `packages/gamut/src/Button/index.tsx`. +- `PascalCase` for components, `camelCase` for props and variables, `SCREAMING_SNAKE_CASE` for true constants. +- First mention is "the `Box` component"; after that, "the component". +- Pluralize the word, not the identifier: "These `Box` components are…", not "These `Boxes` are…". +- Capitalize proper nouns: Codecademy, Storybook, Figma, GitHub. + +**Formatting** + +- Sentence case for every heading, button, and piece of UI text. +- Start at `##` — the `

` comes from `parameters.title`. Never skip a level. +- Numerals for all numbers, commas at thousands: 1,000. +- Space between number and unit in prose ("16 pixels"), none in code (`16px`, `2rem`). +- Bulleted lists take parallel structure. Periods when the items are complete sentences, none when they are fragments. Numbered lists for sequential or ranked items, each starting with a capital letter. +- Fenced code blocks always carry a language identifier (`tsx`, `sh`, `css`). Shell commands use `sh` and omit the `$` prompt. +- Blank lines between sections, never two in a row. +- Spell out an acronym on first use — "Web Content Accessibility Guidelines (WCAG)" — except for terms common to web work: HTML, CSS, API, UI, UX. + +**Pointing at things** + +- Bold for UI labels: the **Show code** button, **Next**, **Close**. +- Describe where a control is by what it looks like, not where it sits: "Click the **Theme Switcher** (paintbrush icon)". +- No directional language. Storybook reflows, so "the form on the right" and "the section above" go stale. Use "the following form", "the adjacent form", "the previous section". +- "Click" is the device-agnostic verb — prefer it over "tap", "press", or "hover over" unless the interaction genuinely differs by input device. +- Link text names the destination, not the action, and stands alone out of context. Link the noun and leave the verb outside it: "See the [Stories page](#)" rather than "[See the Stories page](#)", and never "[Click here](#)". Two to three words minimum, unique within a page. + +## Step 3 — Edit the prose + +Read [`prose-quality.md`](./references/prose-quality.md) and run its pass over everything you wrote. Do this as a separate step with the draft in front of you. Slop is hard to see while composing and easy to see while reading, which is why the order matters. + +The single most useful test from that pass, if you remember nothing else: **if a sentence could move unchanged into the docs for a different component, it is filler.** Replace it with a fact about this component or delete it. + +## Before you finish + +Check your own output against this list: + +1. Every prop, variant, and story export you named exists in the source. Re-grep the ones you are least sure about. +2. Every `` matches a real story id, and every image `src` matches a real file. +3. No fabricated Figma URL. Unresolved values are marked `TODO:` and surfaced in your summary. +4. Headings are sentence case, start at `##`, and skip no levels. +5. Code identifiers are in backticks; the component name is never pluralized. +6. No directional language, no "click here", no `$` in shell blocks. +7. Story examples are self-contained and copy-pasteable — see the **Show code** note in [`storybook-docs.md`](./references/storybook-docs.md). +8. The prose pass ran, and no sentence survives that would fit another component's page. + +Then tell the user what you changed, and list anything you left as `TODO:` along with what it needs — a Figma link, a designer's anatomy export, a status confirmation. diff --git a/packages/gamut/agent-tools/skills/gamut-docs/references/docs-in-code.md b/packages/gamut/agent-tools/skills/gamut-docs/references/docs-in-code.md new file mode 100644 index 0000000000..ddf993a8f0 --- /dev/null +++ b/packages/gamut/agent-tools/skills/gamut-docs/references/docs-in-code.md @@ -0,0 +1,137 @@ +# Documentation in code + +Prop JSDoc, code comments, and naming inside component source files. + +Documenting in the source file creates a single source of truth that stays in sync with the implementation. Prop JSDoc in particular has two audiences beyond the reader of the file: TypeScript surfaces it on hover in the editor, and Storybook renders it into the props table that `` produces on the component's doc page. Writing it is part of documenting the component, not a separate chore. + +## Prop JSDoc + +Use `/** */` blocks on the props type: + +```tsx +export type ButtonProps = { + /** + * The visual style variant of the button. + */ + variant: 'primary' | 'secondary'; + + /** + * Whether the button is disabled. + */ + disabled?: boolean; +}; +``` + +Guidelines: + +- Write full sentences, with a period. +- Start a boolean description with "Whether": "Whether the button is disabled." +- Describe what the prop controls, not its type — TypeScript already prints the type into the table. `variant: 'primary' | 'secondary'` needs "The visual style variant of the button", not "A string that is either primary or secondary". +- Say what a value means when the name does not: if `tipAlignment` defaults to `bottom-center`, note the default. +- Optionality shows up in the table from `?`, so there is no need to write "Optional." +- Use discretion about whether a prop needs a comment. When unsure, include one — a self-evident comment costs a line, while a missing one on a subtle prop costs the reader a trip to the source. + +A comment that restates the prop name adds nothing: + +```tsx +// ❌ Says only what the name already says +/** + * The onClick handler. + */ +onClick?: () => void; + +// ✅ Says when it fires and what it receives +/** + * Called when the user dismisses the alert. Receives no arguments; the parent + * owns the alert's visibility. + */ +onClose?: () => void; +``` + +## Code comments + +Comments explain _why_ code exists, not _what_ it does. Names handle the "what". Reserve comments for decisions a reader would otherwise have to reverse-engineer. + +**Worth a comment** + +Complex logic — the algorithm or the reason for it: + +```tsx +// Use binary search for O(log n) performance on sorted arrays +const index = binarySearch(sortedArray, target); +``` + +Business or spec constraints — the requirement behind the line: + +```tsx +// Per WCAG 2.2, focus must return to trigger element on close +previousFocusRef.current?.focus(); +``` + +Workarounds — what is being worked around, and when it can go: + +```tsx +// Safari doesn't support :focus-visible, fallback to :focus +// TODO: Remove when Safari 15+ is minimum supported version +``` + +Non-obvious decisions — why the strange-looking thing is correct: + +```tsx +// Delay state update to avoid race condition with async validation +setTimeout(() => setIsValid(true), 0); +``` + +**Not worth a comment** + +Self-explanatory code, where a good name already carries the meaning: + +```tsx +// ❌ Bad: Comment restates the code +// Set loading to true +setIsLoading(true); + +// ✅ Good: Code is self-documenting +setIsLoading(true); +``` + +Commented-out code — delete it. Git tracks history, and a commented block leaves the next reader guessing whether it is a work in progress or debris: + +```tsx +// ❌ Bad: Dead code clutters the file +// const oldImplementation = () => { ... }; +``` + +**Style** + +- `//` for single-line comments, with a space after the slashes. +- `/** */` for JSDoc on exports — functions, types, and components. +- Complete sentences with proper punctuation. +- Update the comment when the code changes. A stale comment is more harmful than none, because it is trusted. + +## Naming + +Clear names remove the need for most comments, so naming is the first documentation decision in a file. + +**Variables and constants** + +- `camelCase`: `userName`, `isLoading`, `itemCount` +- Names that reveal purpose: `filteredResults`, not `arr` +- Booleans take an `is`, `has`, `should`, or `can` prefix: `isVisible`, `hasError`, `shouldRender` +- `SCREAMING_SNAKE_CASE` for true constants: `MAX_RETRY_COUNT`, `DEFAULT_TIMEOUT` +- Plurals for arrays and collections: `users`, `menuItems` +- Single letters only in short loops or mathematical operations + +**Functions and methods** + +- `camelCase`, starting with a verb that names the action: `get`, `set`, `fetch`, `handle`, `render`, `calculate` +- Event handlers take a `handle` prefix: `handleSubmit`, `handleClickOutside` +- Functions returning a boolean read as a question: `isValidEmail`, `canAccessResource`, `hasPermission` +- Concise but descriptive: `fetchUserProfile`, not `getUserProfileDataFromAPI` + +**Components** + +- `PascalCase`: `Button`, `UserProfile`, `NavigationMenu` +- The folder matches the component name, and the file inside matches it too: `Button/Button.tsx`, `UserProfile/UserProfile.tsx` +- Names that indicate purpose: `SkipToContent`, `RadialProgress`, `Toggle` +- Avoid `Component`, `Container`, or `Wrapper` without further context — they describe the shape of the code rather than what it does diff --git a/packages/gamut/agent-tools/skills/gamut-docs/references/prose-quality.md b/packages/gamut/agent-tools/skills/gamut-docs/references/prose-quality.md new file mode 100644 index 0000000000..425c69023c --- /dev/null +++ b/packages/gamut/agent-tools/skills/gamut-docs/references/prose-quality.md @@ -0,0 +1,156 @@ +# Prose quality pass + +Run this over everything before returning it. Do it as a separate reading pass with the draft in front of you — these patterns are almost invisible while composing and obvious while reading. + +The goal is documentation that reads like a colleague who knows the component. Generated docs fail in a specific direction: they are grammatical, confident, well-organized, and say nothing a reader could act on. That is the target here, not typos. + +Two modes: + +- **Writing new docs.** There is no author voice to preserve. The voice to match is the surrounding Gamut pages — plain, direct, present tense, friendly without selling. +- **Revising an existing page.** Someone's voice is already there. Make the minimum effective edit: fix the patterns below, and leave sentences that already carry a real point alone. Do not tidy a page into uniformity, and do not rewrite a blunt-but-clear line into a smoother one. Say what you changed and why. + +## The portability test + +The most useful single check, and the one to run first. + +**If a sentence could move unchanged into the docs for a different component, it is filler.** Delete it, or replace it with a fact, mechanism, constraint, or consequence specific to this component. + +```markdown +❌ The Badge component is a versatile and flexible component that empowers +developers to seamlessly display information in a robust way. + +✅ Use `Badge`s to display read-only information like statuses, attributes, +and other emphasized information. +``` + +The first sentence fits Badge, Tag, Alert, Card, and every component in every design system ever built. The second one says what goes inside a badge and that it is not interactive. + +Apply the test hardest to the sections most prone to it: + +- **`### Best practices:`** — "Use consistently", "consider accessibility", and "follow design guidelines" are portable. "Use badges sparingly, as too many can dilute their effectiveness" is not. +- **`### When NOT to use:`** — a bullet has to name the alternative component. "Do not overuse" names nothing. +- **`## Accessibility considerations`** — "ensure sufficient color contrast" is portable and belongs in Foundations, not here. What belongs here is what this component actually does: where focus goes on close, which keys the listbox handles, which element owns the accessible name. + +## Words to cut + +Design-system docs attract these especially hard. Cut them outright: + +> delve, foster, leverage, utilize, facilitate, empower, streamline, robust, seamless, seamlessly, versatile, cutting-edge, paradigm shift, game changer, tapestry, realm, beacon, multifaceted, meticulous, intricate, paramount, transformative, elevate, embark, supercharge, harness, ever-evolving + +Most have a plain replacement: `utilize` → use, `leverage` → use, `facilitate` → let, `streamline` → simplify, `empower … to` → let. Some have no replacement because they carry no meaning — `robust`, `seamless`, and `versatile` describe a feeling about the component rather than anything about it. Delete them and state the capability. + +**Often-empty adverbs:** just, simply, actually, truly, fundamentally, importantly, crucially, inherently, inevitably, literally, honestly. Cut when they add nothing — and note that `simply` and `just` also carry a cost beyond filler: "simply pass the `variant` prop" tells a reader who is stuck that their difficulty is a personal failing. + +**Often-empty phrases:** it's worth noting, it's important to note, at the end of the day, when it comes to, at its core, in today's world, the reality is, the truth is, in terms of, with regard to, in order to, going forward, in this article, let's dive in. Cut them when they delay the point. `in order to` is almost always just `to`. + +## Patterns to cut + +Each is paired with the fix. The rewrite is the point — recognizing the pattern without changing the shape of the sentence does nothing. + +**Binary contrasts.** "This is not X. It's Y." / "The question isn't X, it's Y." / "It's not just X but Y." State Y directly. + +```markdown +❌ Badge isn't about decoration, it's about communicating status. +✅ Use `Badge`s to communicate status. +``` + +**Throat-clearing openers.** "Here's the thing", "Let me be clear", "I'll be honest", "The uncomfortable truth is". Cut and state the point. + +**Faux-insight setups.** "This is the part most people skip", "What most people get wrong", "Here's what nobody tells you". These flatter the writer and tell the reader nothing. Cut the setup and let the claim stand. + +```markdown +❌ Here's what most developers miss: the accent variant ignores ColorMode. +✅ The accent variant does not respond to ColorMode. +``` + +**Colon reveals.** A noun phrase, a colon, then a lowercase dramatic reveal. "The detail that makes it work: a separate agent grades it." Rewrite as a plain sentence. Colons are for lists, labels, and quotes. + +```markdown +❌ The one caveat: this variant doesn't respond to ColorMode. +✅ This variant does not respond to ColorMode. +``` + +**Superficial analysis.** Trailing `-ing` clauses that pretend to explain significance: "highlighting", "underscoring", "reflecting", "showcasing", "ensuring". Replace with the actual consequence. + +```markdown +❌ The variant includes a background color, ensuring better visual hierarchy. +✅ The variant includes a background color, so the badge stays legible when it +overlaps other content. +``` + +**Importance puffery.** "Plays a vital role", "marks a pivotal moment", "is a cornerstone of", "underscores its significance". State the fact and let the reader judge. + +```markdown +❌ The Anatomy section plays a vital role in the design system. +✅ The Anatomy section names each part of the component and its copy limits. +``` + +**Interpretive metadiscourse.** Lines that step outside the subject to tell the reader what to notice or how much weight to give it: "That last part matters more than it sounds", "The key point is", "As you can see", "This distinction matters", "It's important to remember", and redundant "In other words". If the point is already clear, delete the aside. If it is not, add the missing fact instead. + +**Weasel attribution.** "Experts agree", "it's generally recommended", "best practices suggest", "studies show". In Gamut docs, the authority is the design system, the implementation, or a spec — so name it (`Per WCAG 2.2…`, "The `Alert` component handles this by…") or drop the claim. Never invent a source or a spec section number. + +**Fake-strong verbs.** "Serves as", "acts as", "functions as", "provides the ability to". Prefer plain `is`, `has`, and `can`, or a real verb. + +```markdown +❌ The component serves as a centralized container for form field state. +✅ The component holds form field state, validation, and error messages. +``` + +**Synonym cycling.** If the right word is right, repeat it. Gamut's writing guide is stricter than general prose advice here: one term per concept, and the same term across the heading, the body, and the code sample. Rotating between "badge", "label", "chip", and "indicator" for style makes a reader wonder whether four things exist. + +**Negative listing.** "Not a button. Not a link. A badge." Just say what it is. + +**Dramatic fragmentation.** "X. And Y. And Z." / "That's it. That's the whole component." Use complete sentences. + +**Robotic rhythm.** Repeated sentence shapes and identical paragraph structures. This shows up in `## Variants` sections most: six subsections that each open "Use the X variant to …" read as generated. Vary the opening where the variant's actual reason to exist is different, and keep the parallel form where the variants really are parallel. + +**Rhetorical setups.** "What if I told you…", "Think about it:", "Plot twist:", and self-answered "Question? Answer." pairs. Drop them. + +**Fake-profound kickers.** A final "deep" line that turns the point into a metaphor or mic-drop. Delete it rather than rewriting it into a better metaphor, and end on the clearest concrete sentence already there. + +**Summary-recap endings.** "In conclusion", "Ultimately", "Overall", or a closing paragraph that restates the page. Component pages end on `## Playground` and `` — there is no conclusion to write, and adding one is pure padding. + +**Em dashes.** Do not use them as a default rhythm crutch. One or two in a long page are fine when they clearly beat a comma, a period, or parentheses. Remove clusters and decorative dashes. + +## Formatting slop + +- No emoji in headings. +- No bold sprinkled mid-sentence for emphasis. Bold in Gamut docs has a job: UI labels (**Show code**) and the names of parts in an `## Anatomy` list. Using it for emphasis dilutes that signal. +- No heading over a two-sentence section. Fold it into its parent. +- **Bullets are not automatically slop here.** `### Best practices:`, `### When NOT to use:`, and `## Anatomy` part lists are genuinely list-shaped, and the writing guide specifies them. The pattern to catch is a paragraph shredded into fragments — three bullets that only make sense read in order are a paragraph wearing a costume. Format follows content in both directions. +- Sentence case after a colon, unless grammar, a proper noun, a title, or code requires otherwise. + +## Component-doc failure modes + +Beyond general slop, these are specific to documenting components and are worth a targeted look: + +**Restating the props table in prose.** `` already renders every prop, its type, and its JSDoc. A prose section that walks the same list adds a second thing to keep in sync and goes stale first. Write prose where it adds what the table cannot: when to reach for a prop, how two props interact, what happens at a breakpoint. + +**Describing the code sample.** The canvas is right there. "The example below shows a badge with the `variant` prop set to `secondary`" is a caption for something the reader can already see. Use the sentence to say when to use the variant instead. + +**Asserting behavior rather than checking it.** "The modal traps focus and returns it to the trigger on close" is either a documented guarantee or a fabrication, and only the implementation says which. Read it. If you cannot confirm, leave a `TODO:` rather than a confident claim — this is the one category of slop that causes accessibility regressions rather than just bad reading. + +**Hedged non-guidance.** "Consider whether a badge is appropriate for your use case." This is the shape of advice with the advice removed. Either give the criterion or cut the line. + +## Checklist + +Answer each with pass or fail. On any fail, fix the draft and read it again. + +1. Does every sentence fail the portability test — is each one specific to this component? +2. Are the banned words, empty adverbs, and filler phrases gone? +3. Are binary contrasts, negative listings, rhetorical setups, and throat-clearing openers gone? +4. Are faux-insight setups, colon reveals, `-ing` superficial analysis, fake-strong verbs, and dramatic fragments rewritten as plain sentences? +5. Is importance puffery replaced with the plain fact, and weasel attribution either sourced or cut? +6. Is interpretive metadiscourse gone — no lines telling the reader what to notice? +7. Are there no fake-profound kickers and no summary-recap ending? +8. Is one term used per concept, consistently across heading, body, and code? +9. Do `### Best practices:` and `### When NOT to use:` bullets each carry real, specific guidance, with alternatives named and linked? +10. Does the prose avoid restating the props table or narrating the canvas? +11. Is every behavioral and accessibility claim grounded in the implementation, with anything unconfirmed marked `TODO:`? +12. Are em dashes sparse, bold reserved for UI labels and anatomy parts, and headings free of emoji? +13. Does it avoid robotic symmetry — do the variant subsections read as written rather than filled in? +14. Would this read naturally if said aloud to a colleague who is about to use the component? + +--- + +The words, patterns, and checklist above are adapted from the [`no-ai-slop`](https://github.com/petergyang/no-ai-slop) skill by Peter Yang, MIT licensed, Copyright (c) 2026 Peter Yang. Examples and the component-doc sections have been rewritten for Gamut documentation. diff --git a/packages/gamut/agent-tools/skills/gamut-docs/references/storybook-docs.md b/packages/gamut/agent-tools/skills/gamut-docs/references/storybook-docs.md new file mode 100644 index 0000000000..0ab99916b5 --- /dev/null +++ b/packages/gamut/agent-tools/skills/gamut-docs/references/storybook-docs.md @@ -0,0 +1,380 @@ +# Storybook docs: `.mdx`, `.stories.tsx`, and About pages + +Conventions for the two files that document a Gamut component, plus the About pages that index a folder of them. + +## Contents + +- [Where files go](#where-files-go) +- [The two-file split](#the-two-file-split) +- [The `parameters` object](#the-parameters-object) +- [Section backbone for a component page](#section-backbone-for-a-component-page) +- [Writing each section](#writing-each-section) +- [Blocks available from `~styleguide/blocks`](#blocks-available-from-styleguideblocks) +- [Linking](#linking) +- [Story files (`.stories.tsx`)](#story-files-storiestsx) +- [About pages](#about-pages) +- [Editor snippets](#editor-snippets) + +## Where files go + +Component docs live in `packages/styleguide/src/lib/`, and the folder structure is the Storybook hierarchy — what nests on disk nests in the sidebar. The top level follows Gamut's flavor of atomic design: + +``` +packages/styleguide/src/lib/ +├── Atoms/ Badge, Card, Buttons/, FormInputs/, … +├── Molecules/ Alert, Menu, Popover, Modals/, Tips/, … +├── Organisms/ BarChart, DatePicker, ConnectedForm, … +├── Foundations/ ColorMode, theme stories, tokens +├── Layouts/ +├── Typography/ Text, Anchor +├── Meta/ guides and process docs +└── UX Writing/ +``` + +Find the folder that fits the component's complexity, then create a folder named after the component holding both files: + +``` +Atoms/Badge/ +├── Badge.mdx +└── Badge.stories.tsx +``` + +Extra example or utility files can live alongside them in that folder. + +**Naming:** component-related files use the component's `PascalCase` name — `RadialProgress.mdx`. Files that are not about a component use sentence case with real spaces — `General principles.mdx`, `About pages.mdx`. + +**Anatomy images** live in `packages/styleguide/src/static//` and are referenced with a path relative to the static root: `src="./atoms/badge.png"`, `src="./molecules/alertAnatomy.png"`. Check the directory for the file before referencing it; if the export does not exist yet, leave a `TODO:` noting that a designer needs to provide it rather than pointing at a path that 404s. + +## The two-file split + +| File | Holds | +| -------------- | -------------------------------------------------------------------------- | +| `.stories.tsx` | The working examples — every rendered variation, in Component Story Format | +| `.mdx` | The written documentation, metadata, and the prose around each example | + +The `.mdx` file renders stories from the `.stories.tsx` file via ``. Prose never lives in the story file, and examples are never inlined into the `.mdx` file. + +## The `parameters` object + +Every component page exports a `parameters` object that drives the page header, the sidebar entry, and the links out to source and design. Five fields matter: + +| Field | What it holds | +| ---------- | ------------------------------------------------------------------------------------------------------------- | +| `title` | The component name. Used for linking to this story from elsewhere. | +| `subtitle` | What the component does and what it is typically used for, in one or two sentences. | +| `source` | `{ repo, githubLink }` — the source package (`gamut`, `gamut-styles`, …) and a link to the component's source | +| `status` | The health of the component's API. One of the four values below. | +| `design` | `{ type: 'figma', url }` — the Figma file for the component | + +Status values carry a promise to the reader, so pick deliberately rather than defaulting: + +- `current` — stable, recommended for use +- `updating` — in progress, the API may change +- `deprecated` — still supported but slated for deletion; do not use for new work +- `static` — reference material, no active development + +The `githubLink` points at the component's source directory or file on `main`: + +``` +https://github.com/Codecademy/gamut/blob/main/packages//src/ +``` + +Build it from the real path on disk. In VS Code, right-clicking the file and choosing **Copy Remote File Url From…** → `main` produces this directly. + +A complete header: + +```tsx +import { Canvas, Controls, Meta } from '@storybook/addon-docs/blocks'; + +import { ComponentHeader } from '~styleguide/blocks'; + +import * as BadgeStories from './Badge.stories'; + +export const parameters = { + title: 'Badge', + subtitle: + 'Badges are generally used as a standard way to highlight a short piece of text, likely a single word.', + design: { + type: 'figma', + url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/…?node-id=29959-39721', + }, + status: 'current', + source: { + repo: 'gamut', + githubLink: + 'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Badge', + }, +}; + + + + +``` + +`` takes the imported stories namespace, and `` renders the title, subtitle, status chip, and the source and design links. Both belong at the top, in that order, before any `##` heading. + +## Section backbone for a component page + +A good component page gives the reader four things: what it is for, an overview of it working, its discrete variations, and the rules for using it well. + +`## Usage` and `## Playground` appear on nearly every page in the styleguide and are effectively required. The rest earn their place based on the component: + +``` +## Usage +### Best practices: +### When NOT to use: + +## Anatomy (when a labeled Figma export exists) + +## Variants +### + +## Sizes, Icons, States, Color modes, … + +## Playground + +## Accessibility considerations (when there is real behavior to describe) + +## UX writing (when the component contains copy) +``` + +`## Playground` sits near the end. `## Accessibility considerations` and `## UX writing` conventionally sit adjacent to it — either just before or just after — and existing pages do both, so match whatever the neighboring components in the folder do. + +Do not add a heading to hold two sentences, and do not include a section because the skeleton lists it. An `## Anatomy` section with no image, or an `## Accessibility considerations` section restating that buttons are focusable, is worse than its absence. + +## Writing each section + +**`## Usage`** opens with what the component is for, in the form "Use `X` to …": + +```markdown +Use `Badge`s to display read-only information like statuses, attributes, and other emphasized information. +``` + +**`### Best practices:`** is a short bulleted list of real guidance — the things a reviewer would otherwise have to say. Advice that would apply to any component ("use consistently", "consider accessibility") is filler; cut it or make it specific. + +```markdown +### Best practices: + +- Use badges sparingly, as too many can dilute their effectiveness. +- Custom colors should be used sparingly, consistently across similar contexts, and with sufficient contrast for accessibility. +``` + +**`### When NOT to use:`** is the highest-value section on the page and the one most often skipped. Each bullet names the case, then routes to the component that actually handles it, linked: + +```markdown +### When NOT to use: + +- **Interactivity**- when text is meant to be clickable, use the Button or Anchor components instead. +- **Categorization**- for labeling or categorizing content, use the Tag component instead. +``` + +Writing this section requires knowing the neighboring components. Look at what else lives in the folder and in adjacent layers before claiming a boundary. + +**`## Anatomy`** is a labeled image followed by a numbered list of its parts, each with a nested description of its options and any copy guidance: + +```markdown + + +1. **Leading icon** _(optional)_ + +- Use to reinforce the badge's message and improve scannability + +2. **Badge label** + +- Limit 1-2 words +- Display read-only information like statuses, attributes, and other emphasized information +``` + +The `alt` text describes the image and points at the list that explains it, rather than repeating the labels. + +**`## Variants`** gives each variant an `###` subsection: a sentence on when to reach for it, then its canvas. The sentence is the point of the section — a heading followed only by a canvas makes the reader guess. + +```markdown +### Secondary + +Use the secondary variant to display supporting information or less critical statuses. + + +``` + +Reach for `` when a variant carries a caveat worth interrupting for: + +```tsx + + The accent variant does not respond to{' '} + ColorMode. + + } +/> +``` + +**`## Playground`** is the flagship story with its props table — a broad overview of the component's high-level behavior, with the code shown by default so readers can see the shape of a call site immediately: + +```markdown +## Playground + + + + +``` + +When the flagship story is named `Default`, the `of` prop can be omitted. `` renders the props table from the component's TypeScript types and JSDoc — which is why prop JSDoc is part of documentation work, not separate from it. See `docs-in-code.md`. + +## Blocks available from `~styleguide/blocks` + +Import these from `~styleguide/blocks` rather than building equivalents: + +| Block | Use | +| ----------------- | ------------------------------------------------------------------------- | +| `ComponentHeader` | Page header for a component page; takes `{...parameters}` | +| `AboutHeader` | Page header for an About or guide page; takes `{...parameters}` | +| `TableOfContents` | Linked index of child pages | +| `addParentPath` | Helper that prefixes child ids with the parent path for `TableOfContents` | +| `LinkTo` | Internal link to another story by id | +| `Callout` | Inline subtle `Alert` for a caveat; takes a `text` prop (string or node) | +| `ImageWrapper` | Anatomy and example images; takes `src` and `alt` | +| `ImageGallery` | Multiple images shown together | +| `KeyboardKey` | Rendering a key name in keyboard interaction docs | +| `ColorScale` | Rendering a color scale in Foundations pages | +| `TokenTable` | Rendering a design token table | + +From `@storybook/addon-docs/blocks`, component pages use `Meta`, `Canvas`, and `Controls`. + +## Linking + +**Internal** — use `LinkTo` with the target story's id. Ids are exact strings taken from the target's `parameters.id` or ``; open the target file and read it rather than inferring the path. + +```tsx +import { LinkTo } from '~styleguide/blocks'; + +Stories +Animation +``` + +**External** — plain Markdown links, which open in a new tab: + +```markdown +[GitHub Repository](https://github.com/Codecademy/gamut) +``` + +For a link that opens in the current tab, or when more control is needed, use `Anchor`: + +```tsx +Gamut Repository +``` + +Forcing `target="_blank"` needs a reason — readers can open a new tab themselves. When it is warranted, pair it with `rel="noreferrer"`. + +## Story files (`.stories.tsx`) + +Stories follow Storybook's [Component Story Format](https://storybook.js.org/docs/8/api/csf) and its [TypeScript guidance](https://storybook.js.org/docs/writing-stories/typescript). One story per variation or behavior. + +```tsx +import { Badge } from '@codecademy/gamut'; +import type { Meta, StoryObj } from '@storybook/react'; + +const meta: Meta = { + component: Badge, + args: { + variant: 'primary', + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'New', + }, +}; + +export const Secondary: Story = { + args: { + children: 'Beta', + variant: 'secondary', + }, +}; +``` + +**Use concrete values.** Never `foo`, `bar`, or `isBar`. Pick values from a setting the component would really appear in — a boolean is `isModalOpen`, a badge says `Beta`, a label says **Save changes**. Placeholder names make a reader translate before they can evaluate, and they make the copied snippet worse. + +**Keep each story self-contained.** This is the rule that most often gets broken, and the reason is not obvious: when a reader clicks **Show code**, Storybook prints the story's source, and it does not follow abstractions. A story that renders a wrapper component shows the reader the wrapper, not the code they need. Repetition across stories is the correct trade — every story should be copy-pasteable into a project as-is. + +```tsx +// ❌ Show code prints — useless to the reader +export const Default: Story = { + render: (args) => , +}; +``` + +```tsx +// ✅ Show code prints the actual usage +export const Default: Story = { + render: (args) => ( + + Some text that needs info + + + ), +}; +``` + +Prefer `args` alone for simple variations, and add `render` only when the example needs surrounding markup or layout to make sense. + +Story export names become the `###` headings' subjects and the `of={…}` references in the `.mdx` file, so name them after the behavior they show: `Secondary`, `SmallSizeWithIcon`, `WithCloseButton`, `Dismissible`. + +## About pages + +An `About.mdx` file is the landing page for a folder holding several related components — an entry point, not detailed documentation. Create one whenever a folder contains multiple stories. + +Write a short overview of what the folder contains and how the components relate, order the index by importance or usage frequency, and stop there. + +Import each child page's `parameters` and pass them through `addParentPath`, which prefixes the child ids with the parent path so the links resolve: + +```tsx +import { Meta } from '@storybook/addon-docs/blocks'; + +import { + AboutHeader, + addParentPath, + TableOfContents, +} from '~styleguide/blocks'; + +import { parameters as miniParameters } from './Mini.mdx'; +import { parameters as regularParameters } from './Regular.mdx'; + +export const parameters = { + id: 'Atoms/Icons', + title: 'Icons', + subtitle: 'Overview of the icons available in Gamut.', + status: 'current', +}; + + + + + +Provide a general overview of what this collection of components is for. + + +``` + +`addParentPath` takes the parent path (`parameters.id`) and an array of child `parameters` objects. Importing the children's real `parameters` keeps the index in sync automatically — never hand-write the link list when the child pages export their own. + +## Editor snippets + +`.vscode/stories.code-snippets` provides three scaffolds. Mention them to a human who is writing docs by hand: + +- `component-story` — the `.stories.tsx` skeleton +- `component-doc` — the component `.mdx` skeleton +- `toc-story` — an About page skeleton + +The templates in this reference reflect the same structure, so there is no need to expand a snippet when writing files directly. diff --git a/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx b/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx index 5284750a64..0d2a6e11f5 100644 --- a/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx +++ b/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx @@ -85,6 +85,7 @@ Installed into app repos via `gamut plugin install`. Source: [`packages/gamut/ag - `gamut-review` — codebase audit playbook, including `styled()` wrappers that bypass system props and bespoke components that duplicate existing ones - `gamut-component-first` — check for an existing Gamut component before building bespoke UI; the proactive counterpart to `gamut-review`'s audit +- `gamut-docs` — writing and revising documentation: component `.mdx` pages, `.stories.tsx` files, prop JSDoc, and a prose pass that strips AI-slop patterns - `gamut-theming` — theme selection, `GamutProvider`, `theme.d.ts` - `gamut-color-mode` — ColorMode and semantic color - `gamut-buttons` — button atoms, variants, disabled patterns diff --git a/packages/styleguide/src/lib/Meta/Gamut writing guide/About.mdx b/packages/styleguide/src/lib/Meta/Gamut writing guide/About.mdx index 6e8bbba41a..fabadd867b 100644 --- a/packages/styleguide/src/lib/Meta/Gamut writing guide/About.mdx +++ b/packages/styleguide/src/lib/Meta/Gamut writing guide/About.mdx @@ -3,6 +3,7 @@ import { Meta } from '@storybook/addon-docs/blocks'; import { AboutHeader, addParentPath, + Callout, LinkTo, TableOfContents, } from '~styleguide/blocks'; @@ -26,6 +27,8 @@ export const parameters = { + + Welcome to the Gamut writing guide! Thanks for taking the time to learn about our documentation standards. This guide helps keep our documentation clear, consistent, and useful across the Gamut design system. The General principles is a great place to get an overview of our documentation philosophy and best practices. For specific topics like formatting, code documentation, or writing Storybook stories, check out the other pages below. From 17b8fdfe6381847fd15b06885f7e4fc0295eafa0 Mon Sep 17 00:00:00 2001 From: Kenny Lin Date: Fri, 11 Sep 2026 08:57:07 -0400 Subject: [PATCH 2/2] moved doc skill and updated related docs --- .../gamut/agent-tools => .claude}/skills/gamut-docs/SKILL.md | 2 +- .../skills/gamut-docs/references/docs-in-code.md | 0 .../skills/gamut-docs/references/prose-quality.md | 0 .../skills/gamut-docs/references/storybook-docs.md | 0 .../src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) rename {packages/gamut/agent-tools => .claude}/skills/gamut-docs/SKILL.md (93%) rename {packages/gamut/agent-tools => .claude}/skills/gamut-docs/references/docs-in-code.md (100%) rename {packages/gamut/agent-tools => .claude}/skills/gamut-docs/references/prose-quality.md (100%) rename {packages/gamut/agent-tools => .claude}/skills/gamut-docs/references/storybook-docs.md (100%) diff --git a/packages/gamut/agent-tools/skills/gamut-docs/SKILL.md b/.claude/skills/gamut-docs/SKILL.md similarity index 93% rename from packages/gamut/agent-tools/skills/gamut-docs/SKILL.md rename to .claude/skills/gamut-docs/SKILL.md index 17aae21f8b..c6b471b972 100644 --- a/packages/gamut/agent-tools/skills/gamut-docs/SKILL.md +++ b/.claude/skills/gamut-docs/SKILL.md @@ -7,7 +7,7 @@ description: Use this skill when documenting Gamut — writing or revising a com How to write and revise Gamut documentation so it is accurate, consistent with the writing guide, and free of generated filler. -See also: [`gamut-accessibility`](../gamut-accessibility/SKILL.md) — the universal rules to check a component against before writing an `## Accessibility considerations` section, rather than asserting behavior. The component skills (e.g. [`gamut-buttons`](../gamut-buttons/SKILL.md), [`gamut-datatable`](../gamut-datatable/SKILL.md)) carry verified prop and variant detail worth reading before documenting those components. +See also: [`gamut-accessibility`](../../../packages/gamut/agent-tools/skills/gamut-accessibility/SKILL.md) — the universal rules to check a component against before writing an `## Accessibility considerations` section, rather than asserting behavior. The component skills (e.g. [`gamut-buttons`](../../../packages/gamut/agent-tools/skills/gamut-buttons/SKILL.md), [`gamut-datatable`](../../../packages/gamut/agent-tools/skills/gamut-datatable/SKILL.md)) carry verified prop and variant detail worth reading before documenting those components. [`gamut-create-skill`](../gamut-create-skill/SKILL.md) — the blueprint this skill's structure follows; read it before authoring a new skill. Storybook: diff --git a/packages/gamut/agent-tools/skills/gamut-docs/references/docs-in-code.md b/.claude/skills/gamut-docs/references/docs-in-code.md similarity index 100% rename from packages/gamut/agent-tools/skills/gamut-docs/references/docs-in-code.md rename to .claude/skills/gamut-docs/references/docs-in-code.md diff --git a/packages/gamut/agent-tools/skills/gamut-docs/references/prose-quality.md b/.claude/skills/gamut-docs/references/prose-quality.md similarity index 100% rename from packages/gamut/agent-tools/skills/gamut-docs/references/prose-quality.md rename to .claude/skills/gamut-docs/references/prose-quality.md diff --git a/packages/gamut/agent-tools/skills/gamut-docs/references/storybook-docs.md b/.claude/skills/gamut-docs/references/storybook-docs.md similarity index 100% rename from packages/gamut/agent-tools/skills/gamut-docs/references/storybook-docs.md rename to .claude/skills/gamut-docs/references/storybook-docs.md diff --git a/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx b/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx index 0d2a6e11f5..d8558cc9cb 100644 --- a/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx +++ b/packages/styleguide/src/lib/Meta/AI Tooling/Gamut plugin/Best practices.mdx @@ -85,7 +85,6 @@ Installed into app repos via `gamut plugin install`. Source: [`packages/gamut/ag - `gamut-review` — codebase audit playbook, including `styled()` wrappers that bypass system props and bespoke components that duplicate existing ones - `gamut-component-first` — check for an existing Gamut component before building bespoke UI; the proactive counterpart to `gamut-review`'s audit -- `gamut-docs` — writing and revising documentation: component `.mdx` pages, `.stories.tsx` files, prop JSDoc, and a prose pass that strips AI-slop patterns - `gamut-theming` — theme selection, `GamutProvider`, `theme.d.ts` - `gamut-color-mode` — ColorMode and semantic color - `gamut-buttons` — button atoms, variants, disabled patterns @@ -106,6 +105,7 @@ Installed into app repos via `gamut plugin install`. Source: [`packages/gamut/ag Available in this repo only — never exported. These live directly in [`.claude/skills/`](https://github.com/Codecademy/gamut/tree/main/.claude/skills/), read natively by Claude Code and by Cursor via its [Claude-compatibility path](https://cursor.com/docs/skills). - `gamut-create-skill` — blueprint playbook for authoring a new Gamut agent skill from scratch +- `gamut-docs` — writing and revising Gamut's own documentation: component `.mdx` pages, `.stories.tsx` files, prop JSDoc, and a prose pass that strips AI-slop patterns ## Design-to-code (Figma MCP)