Aksel design system skill
Mission
Build correct, accessible, production-quality UI with the Aksel design system (Nav).
Lead the user to the right component, token, and primitive — and back every choice with the
Aksel MCP server, not memory. The MCP (5 tools + 4 resources) returns live data from
aksel.nav.no. Aim for the fewest correct calls.
MCP-First rule (hard rule)
Never generate, edit, or debug Aksel code from training memory alone.
Aksel moves fast — token names, props, and whole components change between majors, and your
weights are stale. Before writing any import, prop, token, or icon name:
- Confirm it exists and how it's used via the MCP.
- Never invent doc paths, prop names, token names, or icon exports.
- MCP wins over memory. If MCP returns nothing after a real attempt, say what you
tried — don't fall back to a guess.
This one rule prevents the most common failure: confidently shipping an Aksel API that no
longer exists.
Preflight: is the MCP available?
This skill is built around the Aksel MCP. Before your first lookup, check whether the
aksel_* tools exist, and branch:
✅ MCP available (strongly preferred). Use the tools below — they return live,
version-correct data (structured props, real token values, verified icon names) you can't
reconstruct from memory.
Note: The url "https://aksel-mcp.nav.no" will need to be whitelisted in your environment to work. If you can't access it, use the fallback below but recommend installing the MCP for a better experience.
❌ MCP not available → recommend installing it, then fall back to fetching docs.
- Recommend the MCP — it removes guesswork and stays current. Install by adding the
server:
"io.github.navikt/aksel-mcp": {
"type": "streamable-http",
"url": "https://aksel-mcp.nav.no/mcp",
"gallery": "https://mcp-registry.nav.no",
"version": "1.0.0"
}
- Until it's installed, fall back to the public LLM docs over HTTP. Start from the
index at
https://aksel.nav.no/llm.md — it lists every page as an individual .md file
— then fetch the specific .md page you need. This is the same content the MCP
serves, so the anti-hallucination rules still apply: read the real page; never invent
paths, props, tokens, or icon names.
Tool → manual-fetch fallback
When the MCP is absent, map each tool to a direct fetch:
| MCP tool (preferred) |
Without MCP, fetch instead |
aksel_find_docs (docs) |
https://aksel.nav.no/llm.md index → follow the matching .md link |
aksel_get_doc |
the page's .md URL directly (e.g. https://aksel.nav.no/komponenter/core/button.md) |
aksel_get_component_info |
the component's .md page — it documents props & usage (prose, not a structured table) |
aksel_get_token_details |
https://aksel.nav.no/grunnleggende/styling/design-tokens.md |
aksel_find_icons |
https://aksel.nav.no/komponenter/ikoner.md |
aksel_find_docs (migrations) |
https://aksel.nav.no/grunnleggende/kode/codemods-config.md + …/migreringsguider/versjon-8.md |
Prefer fetching individual .md pages over the full collection — only pull llm.md (or
komponenter.md) when you need to discover the right page.
Tool capability matrix
Route by intent. Pick the narrowest tool. The default loop is Find → Fetch.
| Intent |
Tool |
Key args |
Returns |
Common failure to avoid |
| Find the doc/component path for a topic |
aksel_find_docs |
{ query, kind:"docs" } |
ranked results[] with path |
Guessing a path instead of searching first |
| Read a full doc page |
aksel_get_doc |
{ path } (exact, from find) |
markdown |
Hand-writing a path; must start /, end .md |
| Get a component's props/API |
aksel_get_component_info |
{ component } slug or path |
structured props |
Reading prose docs when you need the prop table |
| Look up a design token's value/usage |
aksel_get_token_details |
{ tokenName } |
value, accessors, semantics |
Using find_docs for tokens — wrong tool |
| Browse tokens by keyword |
aksel_find_docs |
{ query, kind:"tokens" } |
token catalog matches |
Then call get_token_details for the exact one |
| Find an icon |
aksel_find_icons |
{ keyword, category?, variant? } |
icons[] with name |
Assuming an icon export name from memory |
| Find a migration/codemod |
aksel_find_docs |
{ query, kind:"migrations" } |
codemods + run command |
Using kind:"docs" for upgrade questions |
Resources (read directly when you want the whole catalog, not a search):
aksel-docs://index, aksel-tokens://catalog, aksel-icons://category-catalog,
aksel-migrations://catalog.
Core workflow: Find → Fetch → Build → Validate
Most tasks need 2–4 MCP calls.
- Find —
aksel_find_docs({ query }) with 1–2 keywords (component names work best;
Norwegian and English both work). Use the returned path; never build one yourself.
Switch kind: tokens/colors/spacing → "tokens", upgrades/codemods → "migrations".
- Fetch —
get_doc (usage, examples, props as prose), get_component_info (props as
JSON), get_token_details (one token), find_icons (then map name → ${name}Icon).
- Build — style with tokens + primitive props, never raw hex/px
(tokens-styling.md); reach for layout primitives before
custom CSS (primitives-layout.md); add the a11y props
Aksel requires (accessibility.md).
- Validate — run the checklist at the end of this file.
Decision tree: route the task
The Find → Fetch → Build → Validate loop is how you work; this is where to go for a
given task. Orient once, then take the matching branch(es). Load a reference the first
time a task touches its domain — don't preload all of them. All reference files live in
references/ (e.g. references/components.md).
0. Orient (once per task). Glance at the project before building:
- Are the
aksel_* MCP tools available? If not, see Preflight above — encourage
installing the MCP, and until then fall back to fetching https://aksel.nav.no/llm.md.
- Is Aksel installed and which major version? Check
package.json for @navikt/ds-react
(its version drives which API is current). Unsure about setup, imports, or SSR/RSC?
→ load setup-and-imports.md.
Then branch by intent (reference to load → tools to call):
| If the task is… |
Load |
Then call |
| Pick/build a component (form, modal, table, …) |
components.md |
find_docs → get_component_info → get_doc (usage) → build |
| Implement a Figma design → Aksel code |
figma-to-code.md |
Figma MCP (get_design_context/get_screenshot) → map to Aksel via the aksel_* tools → build |
| Lay out / space / make responsive |
primitives-layout.md |
build with primitives (get_component_info for a primitive's API) |
| Color / token / spacing value / Tailwind styling |
tokens-styling.md |
get_token_details (browse via find_docs kind:"tokens") |
| Light/dark mode or base color |
theming.md |
usually no call — Theme component |
| An icon |
icons.md |
find_icons → map name → ${name}Icon |
| Accessibility / labels / a11y review |
accessibility.md |
get_component_info to find the label/description props |
| Upgrade / codemod / “why is this deprecated?” |
migrations.md |
find_docs kind:"migrations" |
| Project setup / imports / packages / SSR |
setup-and-imports.md |
find_docs kind:"docs" ("kom i gang") |
| A tool call misbehaves / returns nothing |
mcp-workflow.md |
re-route per its recovery tables |
Complex tasks chain branches. “Dark-mode contact form with a trash icon” touches
components.md + tokens-styling.md + theming.md + icons.md + accessibility.md —
load each as you reach that part, and build in that order (structure → style → theme →
icons → a11y pass).
Reference files
The decision tree above says when to load each file; this is the index of what each
contains. Load on demand to keep context small.
- references/mcp-workflow.md — tool mechanics,
kind switching, empty-result recovery, anti-hallucination rules.
- references/setup-and-imports.md — packages, import paths, locale
Provider, SSR/RSC, CSS-only.
- references/primitives-layout.md —
Box/HStack/VStack/HGrid/Page/Bleed, responsive props, spacing & radius scales.
- references/tokens-styling.md — token-props vs
--ax- variables, color roles, styling discipline, Tailwind preset.
- references/theming.md — light/dark mode, the
Theme component, data-color.
- references/components.md — choosing the right component; form & composition patterns.
- references/figma-to-code.md — Figma → Aksel code: Code Connect, confidence matrix, HTML→primitive/token mapping, validation. Needs the Figma MCP.
- references/icons.md — finding icons,
name → ${name}Icon, sizing, a11y.
- references/accessibility.md — required a11y props, semantic structure, Norwegian content.
- references/migrations.md — upgrading versions, codemods, v7→v8 highlights.
Call-budget & token conservation
- Prefer Find → Fetch (2 calls) over scattershot searching; stop once you can build.
find_docs (kind:"docs") needs ≥ 3 characters; if it returns nothing, retry once
with a single broader keyword (a component name) before changing approach.
- Don't re-fetch a doc you already have. Don't echo raw tool JSON back — extract and build.
- ≤ 4 calls is the target, not a limit. Still stuck after ~7? Stop and ask the user rather
than guessing.
Final validation checklist
1---2name: aksel-builder3description: Expert builder for Aksel, the Nav / @navikt design system — React components, design tokens, layout primitives, theming (light/dark), icons, CSS, the Tailwind preset, version migrations, Figma-to-code. Triggers — Aksel, "using/with aksel", Nav/Navikt, "designsystemet", "design system", @navikt/ds-* (e.g. @navikt/ds-react) or @navikt/aksel-* packages; add/create/build/refactor a component (button, input, modal, table, alert, card, form) or layout; implement a design from Figma (pasted figma.com/design/...?node-id link, "implement this design", "build this from Figma", design-to-code). Invoke for frontend UI work with any Aksel signal unless the user opts out.4license: MIT5---67# Aksel design system skill89## Mission1011Build correct, accessible, production-quality UI with the **Aksel design system** (Nav).12Lead the user to the right component, token, and primitive — and back every choice with the13**Aksel MCP server**, not memory. The MCP (5 tools + 4 resources) returns live data from14`aksel.nav.no`. Aim for the _fewest correct calls_.1516---1718## MCP-First rule (hard rule)1920> **Never generate, edit, or debug Aksel code from training memory alone.**2122Aksel moves fast — token names, props, and whole components change between majors, and your23weights are stale. Before writing any import, prop, token, or icon name:24251. **Confirm it exists** and how it's used via the MCP.262. **Never invent** doc paths, prop names, token names, or icon exports.273. **MCP wins** over memory. If MCP returns nothing after a real attempt, say what you28 tried — don't fall back to a guess.2930This one rule prevents the most common failure: confidently shipping an Aksel API that no31longer exists.3233---3435## Preflight: is the MCP available?3637This skill is built around the Aksel MCP. **Before your first lookup, check whether the38`aksel_*` tools exist**, and branch:3940**✅ MCP available (strongly preferred).** Use the tools below — they return live,41version-correct data (structured props, real token values, verified icon names) you can't42reconstruct from memory.4344**Note**: The url "https://aksel-mcp.nav.no" will need to be whitelisted in your environment to work. If you can't access it, use the fallback below but recommend installing the MCP for a better experience.4546**❌ MCP not available → recommend installing it, then fall back to fetching docs.**47481. **Recommend the MCP** — it removes guesswork and stays current. Install by adding the49 server:5051```52"io.github.navikt/aksel-mcp": {53 "type": "streamable-http",54 "url": "https://aksel-mcp.nav.no/mcp",55 "gallery": "https://mcp-registry.nav.no",56 "version": "1.0.0"57}58```59602. **Until it's installed, fall back to the public LLM docs over HTTP.** Start from the61 index at `https://aksel.nav.no/llm.md` — it lists every page as an individual `.md` file62 — then fetch the specific `.md` page you need. This is the **same content** the MCP63 serves, so the anti-hallucination rules still apply: read the real page; never invent64 paths, props, tokens, or icon names.6566### Tool → manual-fetch fallback6768When the MCP is absent, map each tool to a direct fetch:6970| MCP tool (preferred) | Without MCP, fetch instead |71| -------------------------------- | ------------------------------------------------------------------------------------------------ |72| `aksel_find_docs` (`docs`) | `https://aksel.nav.no/llm.md` index → follow the matching `.md` link |73| `aksel_get_doc` | the page's `.md` URL directly (e.g. `https://aksel.nav.no/komponenter/core/button.md`) |74| `aksel_get_component_info` | the component's `.md` page — it documents props & usage (prose, not a structured table) |75| `aksel_get_token_details` | `https://aksel.nav.no/grunnleggende/styling/design-tokens.md` |76| `aksel_find_icons` | `https://aksel.nav.no/komponenter/ikoner.md` |77| `aksel_find_docs` (`migrations`) | `https://aksel.nav.no/grunnleggende/kode/codemods-config.md` + `…/migreringsguider/versjon-8.md` |7879Prefer fetching individual `.md` pages over the full collection — only pull `llm.md` (or80`komponenter.md`) when you need to discover the right page.8182---8384## Tool capability matrix8586Route by _intent_. Pick the narrowest tool. The default loop is **Find → Fetch**.8788| Intent | Tool | Key args | Returns | Common failure to avoid |89| --------------------------------------- | -------------------------- | ---------------------------------- | ------------------------------ | ----------------------------------------------- |90| Find the doc/component path for a topic | `aksel_find_docs` | `{ query, kind:"docs" }` | ranked `results[]` with `path` | Guessing a path instead of searching first |91| Read a full doc page | `aksel_get_doc` | `{ path }` (exact, from find) | markdown | Hand-writing a path; must start `/`, end `.md` |92| Get a component's props/API | `aksel_get_component_info` | `{ component }` slug or path | structured props | Reading prose docs when you need the prop table |93| Look up a design token's value/usage | `aksel_get_token_details` | `{ tokenName }` | value, accessors, semantics | Using `find_docs` for tokens — wrong tool |94| Browse tokens by keyword | `aksel_find_docs` | `{ query, kind:"tokens" }` | token catalog matches | Then call `get_token_details` for the exact one |95| Find an icon | `aksel_find_icons` | `{ keyword, category?, variant? }` | `icons[]` with `name` | Assuming an icon export name from memory |96| Find a migration/codemod | `aksel_find_docs` | `{ query, kind:"migrations" }` | codemods + run command | Using `kind:"docs"` for upgrade questions |9798**Resources** (read directly when you want the whole catalog, not a search):99`aksel-docs://index`, `aksel-tokens://catalog`, `aksel-icons://category-catalog`,100`aksel-migrations://catalog`.101102---103104## Core workflow: Find → Fetch → Build → Validate105106Most tasks need 2–4 MCP calls.1071081. **Find** — `aksel_find_docs({ query })` with 1–2 keywords (component names work best;109 Norwegian and English both work). Use the returned `path`; never build one yourself.110 Switch `kind`: tokens/colors/spacing → `"tokens"`, upgrades/codemods → `"migrations"`.1112. **Fetch** — `get_doc` (usage, examples, props as prose), `get_component_info` (props as112 JSON), `get_token_details` (one token), `find_icons` (then map `name` → `${name}Icon`).1133. **Build** — style with tokens + primitive props, never raw hex/px114 ([tokens-styling.md](references/tokens-styling.md)); reach for layout primitives before115 custom CSS ([primitives-layout.md](references/primitives-layout.md)); add the a11y props116 Aksel requires ([accessibility.md](references/accessibility.md)).1174. **Validate** — run the checklist at the end of this file.118119---120121## Decision tree: route the task122123The Find → Fetch → Build → Validate loop is _how_ you work; this is _where_ to go for a124given task. Orient once, then take the matching branch(es). **Load a reference the first125time a task touches its domain — don't preload all of them.** All reference files live in126`references/` (e.g. `references/components.md`).127128**0. Orient (once per task).** Glance at the project before building:129130- **Are the `aksel_*` MCP tools available?** If not, see **Preflight** above — encourage131 installing the MCP, and until then fall back to fetching `https://aksel.nav.no/llm.md`.132- Is Aksel installed and **which major version**? Check `package.json` for `@navikt/ds-react`133 (its version drives which API is current). Unsure about setup, imports, or SSR/RSC?134 → load **setup-and-imports.md**.135136**Then branch by intent** (reference to load → tools to call):137138| If the task is… | Load | Then call |139| ------------------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------------ |140| Pick/build a component (form, modal, table, …) | `components.md` | `find_docs` → `get_component_info` → `get_doc` (usage) → build |141| Implement a **Figma design** → Aksel code | `figma-to-code.md` | Figma MCP (`get_design_context`/`get_screenshot`) → map to Aksel via the `aksel_*` tools → build |142| Lay out / space / make responsive | `primitives-layout.md` | build with primitives (`get_component_info` for a primitive's API) |143| Color / token / spacing value / Tailwind styling | `tokens-styling.md` | `get_token_details` (browse via `find_docs` `kind:"tokens"`) |144| Light/dark mode or base color | `theming.md` | usually no call — `Theme` component |145| An icon | `icons.md` | `find_icons` → map `name` → `${name}Icon` |146| Accessibility / labels / a11y review | `accessibility.md` | `get_component_info` to find the label/description props |147| Upgrade / codemod / “why is this deprecated?” | `migrations.md` | `find_docs` `kind:"migrations"` |148| Project setup / imports / packages / SSR | `setup-and-imports.md` | `find_docs` `kind:"docs"` (`"kom i gang"`) |149| A tool call misbehaves / returns nothing | `mcp-workflow.md` | re-route per its recovery tables |150151**Complex tasks chain branches.** “Dark-mode contact form with a trash icon” touches152`components.md` + `tokens-styling.md` + `theming.md` + `icons.md` + `accessibility.md` —153load each as you reach that part, and build in that order (structure → style → theme →154icons → a11y pass).155156---157158## Reference files159160The decision tree above says _when_ to load each file; this is the index of _what each161contains_. Load on demand to keep context small.162163- [references/mcp-workflow.md](references/mcp-workflow.md) — tool mechanics, `kind` switching, empty-result recovery, anti-hallucination rules.164- [references/setup-and-imports.md](references/setup-and-imports.md) — packages, import paths, locale `Provider`, SSR/RSC, CSS-only.165- [references/primitives-layout.md](references/primitives-layout.md) — `Box`/`HStack`/`VStack`/`HGrid`/`Page`/`Bleed`, responsive props, spacing & radius scales.166- [references/tokens-styling.md](references/tokens-styling.md) — token-props vs `--ax-` variables, color roles, styling discipline, Tailwind preset.167- [references/theming.md](references/theming.md) — light/dark mode, the `Theme` component, `data-color`.168- [references/components.md](references/components.md) — choosing the right component; form & composition patterns.169- [references/figma-to-code.md](references/figma-to-code.md) — Figma → Aksel code: Code Connect, confidence matrix, HTML→primitive/token mapping, validation. Needs the Figma MCP.170- [references/icons.md](references/icons.md) — finding icons, `name` → `${name}Icon`, sizing, a11y.171- [references/accessibility.md](references/accessibility.md) — required a11y props, semantic structure, Norwegian content.172- [references/migrations.md](references/migrations.md) — upgrading versions, codemods, v7→v8 highlights.173174---175176## Call-budget & token conservation177178- Prefer **Find → Fetch** (2 calls) over scattershot searching; stop once you can build.179- `find_docs` (`kind:"docs"`) needs **≥ 3 characters**; if it returns nothing, retry once180 with a single broader keyword (a component name) before changing approach.181- Don't re-fetch a doc you already have. Don't echo raw tool JSON back — extract and build.182- ≤ 4 calls is the target, not a limit. Still stuck after ~7? Stop and ask the user rather183 than guessing.184185---186187## Final validation checklist188189- [ ] Every Aksel component and icon used was confirmed via MCP (not memory).190- [ ] Doc paths came from `aksel_find_docs` / `aksel-docs://index` — none were hand-written.191- [ ] Props were checked with `aksel_get_component_info` where non-trivial.192- [ ] Tokens are real (`aksel_get_token_details`); no raw hex or pixel values.193- [ ] Icon imports use the `${name}Icon` export from `@navikt/aksel-icons`.194- [ ] Required accessibility props are present (labels, descriptions, alt text).195- [ ] Imports resolve to the correct `@navikt/*` package and are React 17-compatible.