name: graffiti-best-practices description: Use when generating or refactoring Graffiti UI markup so output is class-first, semantic, accessible, responsive, and aligned with current Graffiti capabilities. metadata: version: 1.9.0
Graffiti Best Practices
Generate high-quality Graffiti markup using current library constraints.
Graffiti is the baseline design layer when this skill is active: it provides the default system for elements, layout primitives, utilities, components, and color/theme tokens.
This skill extends Graffiti patterns; it does not create a parallel styling system.
This skill is strict about class-first composition, minimal inline style usage, semantic HTML, and repeatable quality checks.
It is also strict about template-first adaptation: when a matching hosted template baseline exists, start from that template and customize it.
Quality bar: passing the markup contract is necessary but not sufficient. Output must also clear an aesthetic bar (voice, density, imagery, restraint). Markup that satisfies every contract check but ships with AI-shaped content (generic copy, decorative gradients, invented SVG icons, fake numbers, emoji-as-icons) still fails. See references/AESTHETIC_GUIDE.md.
Audience: developers using Graffiti in real applications and websites.
Package Structure
SKILL.md- activation rules, boundaries, and execution workflowreferences/OUTPUT_CONTRACT.md- required response structure and scoring gatesreferences/GRAFFITI_SYSTEM.md- Graffiti identity, token/variable model, and source-of-truth lookup rulesreferences/COMPONENT_INTENT_MATRIX.md- role boundaries for core components (use-when, do-not-use, fallback)references/CANONICAL_SNIPPETS.md- critical pattern snippets that must be used as first-choice baselinesreferences/RECIPES_LAYOUTS.md- page shell and layout recipesreferences/RECIPES_SECTIONS.md- section-level recipes by intentreferences/RECIPES_COMPONENTS.md- component-level recipe snippetsreferences/ANTI_PATTERNS.md- failure mode catalog and detection heuristicsreferences/RECOVERY_TRANSFORMS.md- deterministic rewrite transformsreferences/EXAMPLES.md- prompt/response behavior examplesreferences/TROUBLESHOOTING.md- ambiguous prompt handling and recovery guidancereferences/AESTHETIC_GUIDE.md- taste / voice / density / imagery / anti-slop rules (output quality)references/THEMING_AND_TOKENS.md- color-scheme and opaque-scale gotchas, theme override chainreferences/MOBILE_AND_CONTAINERS.md- container queries vs media queries, drawer pattern, dvh + safe-areareferences/CHEAT_SHEET.md- fast "I want X, use Y" lookup
Trigger Conditions
Activate this skill when the request includes one or more of these signals:
- Build or refactor a page/section using Graffiti classes
- Convert inline-styled markup to class-first markup
- Produce template-like structures (landing, dashboard, blog, settings, chat, forms, navigation)
- Improve markup quality, semantics, responsiveness, or consistency for Graffiti outputs
Do Not Activate For
- Non-UI/backend-only tasks
- Requests to design a brand new CSS framework or token system
- Requests where Graffiti is not the target styling system
Hard Boundaries (Refusal + Redirect Rules)
- Do not invent class names not present in current Graffiti CSS/docs.
- Do not invent CSS custom properties (
--*) that are not part of Graffiti's documented token/component contracts. - Use Graffiti-first composition: exhaust existing classes/tokens before adding custom CSS.
- Custom CSS is allowed when needed, but apply it systemically: create reusable project-level classes/utilities instead of one-off component-local overrides.
- Do not hardcode raw color literals (
#hex,rgb(),oklch(...)) inline unless explicitly requested. - Do not add JS/Svelte state for purely static structure; add state only when interaction or data requirements actually need it.
- If a requested pattern is not supported by current classes/tokens, return the closest canonical fallback and clearly note the limitation plus an optional reusable extension path.
- If a matching hosted template baseline exists for the requested intent, use it as the baseline. Do not generate an unrelated structure from scratch unless the user explicitly asks for a fresh build.
- Do not re-implement built-in Graffiti primitives with bespoke wrappers/CSS/JS. This is a hard fail for critical patterns including dialog/modal, card/card-link, bubble/chat, callout, input-group, form rows/actions.
- Do not create a duplicate design system (new ad-hoc utility framework, component framework, or token family) when Graffiti primitives already satisfy the request.
- Do not ship AI-shaped content: generic feature-grid copy, fake testimonials, invented stat numbers, emoji-as-icons, hand-drawn SVG illustrations more complex than a basic shape, "Coming soon" decorative badges, gradient backgrounds on every section. See
references/AESTHETIC_GUIDE.mdfor the full anti-slop list. - Do not inline-override hue tokens (
--blue,--red, etc.) and expect their opaque scales (--blue-opaque-N) to follow. Use a theme class instead. Seereferences/THEMING_AND_TOKENS.md. - Do not use
@mediaqueries for component-internal responsiveness. Default to@containerqueries withcontainer-type: inline-size. Seereferences/MOBILE_AND_CONTAINERS.md.
Graffiti Identity (Non-Negotiable)
Treat these as fixed truths for this skill:
- Graffiti is a class-first drop-in CSS system. Markup should compose existing classes instead of rebuilding primitives with inline CSS.
- Graffiti is the baseline layer for page styling in projects using this skill (elements + layouts + utilities + components + full token system).
- Graffiti is a substrate, not a look. The framework's value comes from re-skinning, not from the default look being everything. Output quality depends on the content + assets + tokens the markup carries — slop in, slop out.
- The source of truth is hosted Graffiti docs/templates plus local package evidence, in this order:
https://graffiti-ui.com/base,https://graffiti-ui.com/utilities,https://graffiti-ui.com/elements,https://graffiti-ui.com/ui-blocksrequested withAccept: text/markdown, plus topic routes likehttps://graffiti-ui.com/elements/buttonshttps://graffiti-ui.com/templatesand template pages such ashttps://graffiti-ui.com/templates/landing,https://graffiti-ui.com/templates/dashboard,https://graffiti-ui.com/templates/blog,https://graffiti-ui.com/templates/settings,https://graffiti-ui.com/templates/ai-chat,https://graffiti-ui.com/templates/docs-portal- Installed Graffiti stylesheet/package exports in the target project (actual selectors and variable contracts)
- Skill recipes in
references/*(guidance layer, never higher authority than source files)
- Graffiti variables are contracts, not free-form style knobs. Use documented tokens/overrides only.
- If guidance in skill references conflicts with source files, source files win.
System-First Preflight (Required)
Before writing or editing markup, run this preflight in order:
- Variables: identify required tokens from
https://graffiti-ui.com/basemarkdown and installed Graffiti CSS contracts. - Theme: confirm theme/color intent can be solved with Graffiti tokens/classes first. If theming a non-default surface, consult
references/THEMING_AND_TOKENS.mdto pick the right override level (theme class vs--primaryswap vs component override token). - Layout: map top-level structure to existing layout primitives. For mobile-responsive layout primitives, default to
@containerperreferences/MOBILE_AND_CONTAINERS.md. - Utilities: map text/state/alignment behavior to existing utilities.
- Components: map each requested UI piece to canonical built-in components.
If any category cannot be mapped, choose a documented fallback and record it before writing final markup.
Aesthetic-First Preflight (Required for content surfaces)
For any surface that includes content (marketing, dashboard with copy, templates, blog, etc.) ALSO run this preflight per references/AESTHETIC_GUIDE.md:
- Theme: pick a theme class FIRST. Decide whether it lives globally (on
<html>/ app root) or locally (on a section/feature root). Default-canvas-with-no-theme is a deliberate choice, not a fallback. Seereferences/THEMING_AND_TOKENS.md. - Voice: commit to a tone (editorial / technical / product / luxury / brutalist / etc.) and stay in it.
- Density: declare the per-section content budget (e.g. hero = 1 headline + 1 subhead + 2 CTAs; feature grid = 3–6 items, each ≤30 words). If unsure, default LOW.
- Imagery: decide what every image slot actually contains. If no real imagery is available, use
.box.invisible+gradient-*+ monospace label as placeholder. Never invent decorative SVGs. - Icons: Graffiti ships no icon set and blesses none. Use whatever icon library the project already includes (check
package.jsonand existing imports first). If the project has no icon library, use the<span class="icon" aria-hidden="true">placeholder pattern with a single Unicode glyph until a real icon is supplied. Never invent SVG icons more complex than a basic shape (circle / square / arrow / chevron / plus / minus / X / check). Never use emoji as feature icons. Never mix icon sets in one page.
If theme / voice / density / imagery is ambiguous in the user's request and they didn't provide guidance, ASK before generating — do not silently choose generic SaaS defaults.
Component Intent Steering (Required)
Classes are role-bearing primitives, not generic visual wrappers.
Specific-Over-Generic Rule (the anti-generic-container rule)
.box, .stack, .cluster, .split, .surface are fallbacks, not defaults. Reaching for them when a role-specific primitive exists is the single largest source of ugly, generic-looking Graffiti output.
Before composing any container with a neutral wrapper, walk the role menu in this order and stop at the first match:
- Is this a metric/KPI readout? →
.stat-card - Is this a feature/value-prop entry? →
.feature-card - Is this a record-like content unit (article, product, plan, recipe, post preview)? →
.card(or.card.featured/.card.linked) - Is this a notice / contextual message (info, warning, error, success)? →
.callout+ semantic variant - Is this a chat / message turn? →
.chat-thread+.chat-row+.bubble - Is this a tool call / activity log entry / function invocation? →
.log-card - Is this a compact disclosure (FAQ-style)? →
<details class="bordered">+<summary> - Is this a modal/confirmation surface? → native
<dialog>+.close - Is this a form field row / submit row / option label? →
.row/.form-actions/.form-option-row - Is this an input coupled with one button? →
.input-group - Is this a multi-line message composer with toolbar? →
<form class="composer"> - Is this a TOC for a long document? →
.toc - Is this a vertical icon-only nav rail? →
.icon-rail - Is this a secondary inspector/workbench pane in an app shell? →
.workbench-panelinside.layout-rail.with-workbench - Is this a selectable pill / filter / suggestion? →
.chip(witharia-pressed) - Is this a status pill / category label? →
.tag(semantic variant first) - Is this a pull quote? →
<blockquote class="pull-quote"> - Is this a section eyebrow / overline? →
.eyebrow
Only after exhausting the role menu is .box / .stack / .cluster / .split / .surface allowed — and even then, justify why no role primitive fits.
Three-fit validation (apply once a role primitive is selected):
- Intent fit: component role matches the requested job.
- Semantic fit: host element semantics match the pattern.
- Shape fit: content shape (single metric, repeating article, form control row, notice, etc.) matches the primitive.
Hard boundaries:
.cardand.card.featuredare for repeating content units (article/product/plan-like records), not generic section/page wrappers..card.linkedis for card-as-link preview units, not nav containers or generic anchor wrappers..stat-cardis for KPI/metric values, not long-form copy or feature marketing blurbs..feature-cardis for feature-list entries, not arbitrary content blocks..log-cardis for activity entries (tool calls, deploys, audit lines), not generic cards..composeris for multi-line message composition with a toolbar; do not reach for it as a generic form wrapper..icon-railand.workbench-panelbelong inside.layout-railshells; do not use them as standalone decoration.- Neutral wrappers are fallbacks. If you used
.box/.stack/.surfaceand the result looks unstyled, you probably skipped the role menu above — go back and reselect.
Variable System Rules
When using inline custom properties or token references:
- Prefer core tokens documented on
https://graffiti-ui.com/basemarkdown and implemented in Graffiti CSS contracts. - Prefer semantic status classes before color token overrides (for example
.tag.successbefore--tag-color). - Use component override vars only where documented (for example
--button-color,--tag-color,--bubble-*,--toggle-color,--callout-*). - Do not invent new
--*variable names in markup unless the user explicitly requests a project extension path. - For spacing/layout, prefer existing layout classes first; if override is still needed, use documented variables such as
--gap,--layout-gap,--min-card-width,--max-width. - To re-color a whole surface, prefer applying a theme class (
class="theme-X") over inline-overriding hue tokens. Inline hue overrides do not propagate to opaque scales. Seereferences/THEMING_AND_TOKENS.md. - When inline-overriding
--bgor--fg, also pincolor-schemeon the same element to preventlight-dark()resolution drift.
Theming Decision (Required Before Markup)
Pick the lowest level that solves the requested change. Decide once, apply once.
| Scope of change | Apply theme at | Why |
|---|---|---|
| Whole application / site has a brand identity | class="theme-X" on <html> (or <body>) |
One source of truth; every nested surface inherits |
| One section/feature needs a distinct visual mode | class="theme-X" on the section/feature root |
Triggers per-hue opaque-scale re-derivation locally |
| Only the accent color needs to shift | style="--primary: ..." on the section root |
Smallest correct override; safe inline |
| One component needs a documented variant tweak | Component override token (e.g. --bubble-bg, --callout-tint) |
Respects theme contract |
| Status/role color (success / warning / error / info) | Semantic class variant (.tag.success, .callout.error, etc.) |
Semantic variants always beat color overrides |
Available stock themes (verify against src/lib/themes/ before referencing):
theme-editorial, theme-paper, theme-system, theme-soft-consumer, theme-neon-arcade, theme-studio, theme-signal, theme-lumen. The unstyled canvas (no theme class) is also a valid choice — pick it deliberately, not by accident.
Hard rules:
- Do not inline-override a hue token (
--blue,--red, etc.) and expect the opaque scales (--blue-opaque-N) to follow. Use a theme class instead. - Do not scatter theme decisions across many elements. Apply theme high; let inheritance handle the rest.
- Do not use
!importantto force a token to win — that always means the structural choice was wrong.
Required Workflow
Follow this sequence every time.
Classify intent
- Map request to one or more intents: layout shell, neutral container, nav, form, repeating content unit, table/data, chat, utility/text.
Resolve hosted template baseline first
- Check
https://graffiti-ui.com/templates/*for the closest intent match. - If a match exists, treat it as the required starter and preserve its canonical section/component composition unless the user asked to replace it.
- Record the selected template path in the output contract.
- Check
Run system-first preflight
- Complete variable/theme/layout/utilities/components checks from
references/GRAFFITI_SYSTEM.md.
- Complete variable/theme/layout/utilities/components checks from
Run aesthetic-first preflight (content surfaces)
- For any output that includes content/copy/imagery, declare voice / density / imagery / icons / theme up front per
references/AESTHETIC_GUIDE.md. - If the user's brief doesn't fix these choices, ask before generating.
- For any output that includes content/copy/imagery, declare voice / density / imagery / icons / theme up front per
Resolve source-of-truth class and variable contracts
- For requested components/sections, read
https://graffiti-ui.com/base,https://graffiti-ui.com/utilities,https://graffiti-ui.com/elements, andhttps://graffiti-ui.com/ui-blockswithAccept: text/markdownand use documented classes/examples as canonical patterns. - Prefer topic routes (for example
https://graffiti-ui.com/elements/buttons) when you only need one topic to reduce context noise. - Use
https://graffiti-ui.com/basemarkdown for global token names and categories. - If uncertain about availability, confirm against selectors/variables in installed Graffiti CSS.
- For requested components/sections, read
Create primitive mapping before coding
- For each requested component/interaction, map user intent to an existing Graffiti primitive and cite source file.
- If no direct primitive exists, map to closest fallback and record limitation.
- Use
references/CHEAT_SHEET.mdas a fast lookup when the intent is unambiguous.
Run component intent fit check
- Validate each selected component class against
references/COMPONENT_INTENT_MATRIX.mdbefore writing markup. - If a component only matches visual styling (but not role), remap to the proper primitive.
- Treat role mismatch as a blocking error, not a stylistic preference.
- Validate each selected component class against
Resolve canonical snippets for critical patterns
- For dialog/modal, card/link, bubble/chat, form actions/options, input-group, and callouts, start from
references/CANONICAL_SNIPPETS.mdbaselines.
- For dialog/modal, card/link, bubble/chat, form actions/options, input-group, and callouts, start from
Select canonical recipe path
- Choose the closest reference pattern from:
references/OUTPUT_CONTRACT.mdreferences/RECIPES_LAYOUTS.md,references/RECIPES_SECTIONS.md,references/RECIPES_COMPONENTS.md,references/CANONICAL_SNIPPETS.md
- Recipes refine the baseline template; they do not replace it when a baseline exists.
- Choose the closest reference pattern from:
Build a class plan before writing markup
- Identify layout classes, component classes, utility classes, semantic wrappers, and ARIA/state attributes.
- Record why each role-specific component was selected and why neutral wrappers were rejected.
- Build a variable plan: list every
--*you intend to use and cite its source file.
Plan responsive behavior
- Decide whether each responsive concern is page-shaped (
@media) or component-shaped (@container). Default to@containerfor component internals perreferences/MOBILE_AND_CONTAINERS.md. - Use
[popover].drawer+.drawer-togglefor mobile menus; do not write a JS-toggled menu. - Use
100dvh(not100vh) for any "fill the visible viewport" need.
- Decide whether each responsive concern is page-shaped (
Apply class-first decision tree
- If a class exists, use it.
- Inline style is only allowed for approved token overrides or bounded layout exceptions (see output contract).
- If custom CSS is required, prefer reusable extension classes over local one-off overrides.
Write semantic structure first, then style with classes
- Use landmarks and semantic tags (
header,nav,main,section,article,aside,footer, lists, tables, labels).
- Use landmarks and semantic tags (
Run accessibility minimum checks
- Labels, heading order, keyboard-relevant semantics,
aria-current/state attributes, table semantics, media alt text.
- Labels, heading order, keyboard-relevant semantics,
Run responsiveness checks
- Ensure composition degrades correctly at narrow widths using existing layout primitives + container queries.
Run class and variable validation checks
- Every class in output must be traceable to hosted docs markdown, hosted templates, or installed Graffiti CSS.
- Every inline
--*override must map to documented Graffiti token/override names. - Every role-specific component must pass intent-fit validation from
references/COMPONENT_INTENT_MATRIX.md. - Confirm no built-in primitives were re-implemented with custom wrappers/CSS/JS.
- Confirm no duplicate utility/component/token system was introduced.
Run aesthetic compliance checks
- Run the anti-slop checklist from
references/AESTHETIC_GUIDE.md. - Confirm no AI-shaped content (generic copy, decorative gradients on every section, emoji icons, fake numbers, hand-drawn SVG illustrations).
- Confirm voice is consistent throughout the output.
- Run the anti-slop checklist from
Emit output using required contract
- Response must follow
references/OUTPUT_CONTRACT.mdsection order. - Must include a post-edit compliance report proving no duplicate system was created.
- Response must follow
Handle ambiguity with deterministic defaults
- Apply troubleshooting defaults from
references/TROUBLESHOOTING.md. - Prefer safe class-first fallback plus explicit limitation note over invented classes.
- Apply troubleshooting defaults from
Hosted Template Baseline Map
Use this map before writing markup:
- Landing/marketing pages ->
https://graffiti-ui.com/templates/landing - Dashboard/admin pages ->
https://graffiti-ui.com/templates/dashboard - Blog/content pages ->
https://graffiti-ui.com/templates/blog - Settings/account pages ->
https://graffiti-ui.com/templates/settings - AI chat pages ->
https://graffiti-ui.com/templates/ai-chat - Docs/knowledge-base pages ->
https://graffiti-ui.com/templates/docs-portal
If no direct match exists, use recipes as primary source and state "No baseline template match found" in the output contract.
Class-First Decision Tree
Is there an existing Graffiti class or class combination for this requirement?
- Yes: use class-based implementation.
- No: go to step 2.
Can this be represented as an approved token override?
- Yes: use inline custom properties only (for example
--gap; use--tag-coloronly for custom categories when semantic tag variants do not fit). - No: go to step 3.
- Yes: use inline custom properties only (for example
Is it a bounded layout exception (for example one-off max width wrapper)?
- Yes: allow minimal inline declaration and record rationale in verification.
- No: return closest class-based fallback and document limitation + reusable extension option.
Status defaults:
- Prefer semantic tag variants for status labels:
.tag.success,.tag.warning,.tag.error,.tag.info. - Use
--tag-coloras a fallback for non-status category colors. - Prefer
.card.linkedfor card-as-link patterns instead of inline link reset styles. - Prefer
.form-option-rowfor checkbox/radio label rows instead of inlinedisplay: inline-flexrecipes. - Prefer
.rowinside forms/fieldsets for field wrappers (label + input + help text) instead of repeatedstack+--gapcompositions. - Prefer
.form-actionsfor submit/cancel rows instead of bareclustercompositions (provides responsive stacking). - Prefer native
<dialog>+.closefor modal flows before custom modal wrappers or JS toggles.
Accessibility Minimums (Must Pass)
- Landmark structure is present for page-level outputs.
- Heading hierarchy is logical (no skipped levels without rationale).
- Every form control has an accessible label.
- Interactive nav/menu patterns include state signals (
aria-current,open, checked state). - Tables use proper
table,thead,tbody,th, andtdsemantics.
Compatibility Policy
- Default output is framework-agnostic HTML.
- If user asks for Svelte, keep markup-first output and only add Svelte logic when interaction or data-binding is explicitly required.
- For static structure in Svelte files, prefer markup-first output without unnecessary script/state blocks.
Pass/Fail Gates
Treat output as failed if any hard fail occurs:
- Unknown class names
- Unknown custom property names
- Re-implementation of built-in Graffiti primitives
- Role-specific component used outside its intended role boundary (for example
.cardas generic wrapper) - Duplicate styling system introduced instead of Graffiti composition
- Disallowed inline styles
- Missing required semantic/accessibility structure
- Response does not follow output contract sections
- Matching hosted template exists but output does not use it as baseline (unless user explicitly requested a fresh build)
- AI-shaped content shipped (generic feature copy, fake testimonials, emoji icons, decorative gradients on multiple sections, hand-drawn SVG illustrations)
- Inline hue token override expected to propagate to opaque scales (without a theme class)
@mediaquery used for component-internal responsiveness when@containerwas appropriate
Treat output as pass only if all sections in references/OUTPUT_CONTRACT.md pass their checks AND the aesthetic checklist in references/AESTHETIC_GUIDE.md is clear.
References
references/OUTPUT_CONTRACT.mdreferences/GRAFFITI_SYSTEM.mdreferences/COMPONENT_INTENT_MATRIX.mdreferences/CANONICAL_SNIPPETS.mdreferences/RECIPES_LAYOUTS.mdreferences/RECIPES_SECTIONS.mdreferences/RECIPES_COMPONENTS.mdreferences/ANTI_PATTERNS.mdreferences/RECOVERY_TRANSFORMS.mdreferences/EXAMPLES.mdreferences/TROUBLESHOOTING.mdreferences/AESTHETIC_GUIDE.mdreferences/THEMING_AND_TOKENS.mdreferences/MOBILE_AND_CONTAINERS.mdreferences/CHEAT_SHEET.md