mirror of
https://github.com/navikt/aksel.git
synced 2026-10-12 16:52:03 +00:00
Merge branch 'main' into new-component-coachmark
This commit is contained in:
206 files changed
+5062
-3590
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@navikt/ds-css": patch
|
||||
---
|
||||
|
||||
Search: Icon more visible in forced-colors mode
|
||||
@@ -5,6 +5,7 @@
|
||||
- Narrow search space; don't replace reading code.
|
||||
- Before editing: read target file + nearest related files (`index.ts`, story, test, exports).
|
||||
- Prefer known paths/scripts over broad searches.
|
||||
- User often edits files between turns. Before reviewing/refactoring, re-read the files (or `git diff`); never rely on earlier reads.
|
||||
- Ignore release/deploy/changelog/ops scripts unless asked.
|
||||
|
||||
## Behavior
|
||||
@@ -15,7 +16,7 @@
|
||||
- Answer-first. 0-2 sentences max.
|
||||
- No thinking aloud; do next edit. Explain only when asked or non-obvious.
|
||||
- Show only what changes. Minimal diffs; don't paste whole files.
|
||||
- Edits scoped to one file unless asked. Ask only when truly blocked.
|
||||
- Edit only the files the change needs (see Definition of done); no unrelated files. Ask only when truly blocked.
|
||||
- Favor perf/security; avoid re-renders, top-level side effects, unsafe HTML, env var leaks.
|
||||
|
||||
## Repo summary
|
||||
@@ -27,12 +28,13 @@
|
||||
## Start and validate
|
||||
|
||||
1. `corepack enable`
|
||||
2. `corepack yarn install` (needs `NPM_AUTH_TOKEN` with `read:packages`). Without a token: `corepack yarn workspaces focus @navikt/aksel-icons @navikt/ds-tokens @navikt/ds-css @navikt/ds-react @navikt/ds-tailwind @navikt/aksel @navikt/aksel-stylelint`
|
||||
2. `corepack yarn install` (needs `NPM_AUTH_TOKEN` with `read:packages`). Without a token: `corepack yarn workspaces focus aksel @navikt/aksel-icons @navikt/ds-tokens @navikt/ds-css @navikt/ds-react @navikt/ds-tailwind @navikt/aksel @navikt/aksel-stylelint`
|
||||
3. `corepack yarn boot` (builds all public packages; rerun after `corepack yarn clean`)
|
||||
|
||||
- Run agent commands through `corepack yarn`. Order: existing root/workspace script → Yarn-local binary → add a pinned dependency only when new tooling is required.
|
||||
- Never use `npx`, `pnpx`, `pnpm dlx`, `yarn dlx`, global binaries or curl-piped installers. Public consumer docs (`npx @navikt/aksel`) are intentional; keep them.
|
||||
- If Corepack/Yarn fails because of user-level configuration, report the exact error. Never edit user-home config.
|
||||
- Under the `navikt/cplt` sandbox, `Parse error when loading ~/.yarnrc.yml` means the home rc file is unreadable (EPERM), not a repo issue. Stop retrying, report it and ask the user to fix it outside the sandbox. Don't touch the repo `.yarnrc.yml`.
|
||||
- Choosing checks: use the `aksel-local-validation` skill. Changed-file lint first, then the affected workspace, then root suites only when needed.
|
||||
|
||||
## Definition of done
|
||||
@@ -80,6 +82,7 @@ Path-specific rules load automatically from `.github/instructions/` (see each `a
|
||||
## Coding defaults
|
||||
|
||||
- Small diffs. No drive-by refactors or new deps unless asked.
|
||||
- Propose the simplest maintainable solution first (explicit lists, member-name checks). No TS compiler API/AST tricks in tests (slow). Ask before escalating complexity.
|
||||
- Copy nearest existing pattern before writing new code.
|
||||
- Stable public APIs. Prefer additive props/exports.
|
||||
- `node:` imports for Node builtins. Never import from `esm/`/`cjs/` output.
|
||||
|
||||
@@ -15,7 +15,14 @@ updates:
|
||||
registries:
|
||||
- npm-github
|
||||
groups:
|
||||
next:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "@next/eslint-plugin-next"
|
||||
- "@next/bundle-analyzer"
|
||||
- "next"
|
||||
sanity:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "sanity"
|
||||
- "sanity-*"
|
||||
@@ -23,17 +30,55 @@ updates:
|
||||
- "next-sanity"
|
||||
- "@portabletext/*"
|
||||
eslint:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "eslint"
|
||||
- "@eslint/*"
|
||||
react:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "react"
|
||||
- "react-dom"
|
||||
storybook:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*storybook*"
|
||||
vitest:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "vitest"
|
||||
- "@vitest/*"
|
||||
# Security updates ignore version-update groups, so mirror them to keep lockstep packages together.
|
||||
next-security:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "@next/eslint-plugin-next"
|
||||
- "@next/bundle-analyzer"
|
||||
- "next"
|
||||
sanity-security:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "sanity"
|
||||
- "sanity-*"
|
||||
- "@sanity/*"
|
||||
- "next-sanity"
|
||||
- "@portabletext/*"
|
||||
eslint-security:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "eslint"
|
||||
- "@eslint/*"
|
||||
react-security:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "react"
|
||||
- "react-dom"
|
||||
storybook-security:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "*storybook*"
|
||||
vitest-security:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "vitest"
|
||||
- "@vitest/*"
|
||||
|
||||
@@ -17,6 +17,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
|
||||
@@ -14,15 +14,15 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
prebuild-app-for-playwright:
|
||||
name: Build app for Playwright
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write # Push E2E image to GHCR
|
||||
env:
|
||||
IMAGE_NAME: ${{ github.repository }}-e2e
|
||||
REGISTRY: ghcr.io
|
||||
@@ -31,6 +31,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup and build Next.js application
|
||||
uses: ./.github/actions/build-website
|
||||
@@ -61,6 +62,9 @@ jobs:
|
||||
needs: [prebuild-app-for-playwright]
|
||||
name: Playwright shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read # Pull E2E image from GHCR
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.61.1-noble
|
||||
services:
|
||||
@@ -81,6 +85,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
|
||||
@@ -24,6 +24,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -41,6 +42,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -58,6 +60,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -78,6 +81,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -22,14 +22,14 @@ jobs:
|
||||
name: Run Chromatic
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
pull-requests: write
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -33,6 +33,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
ref: main
|
||||
|
||||
- name: Setup project
|
||||
@@ -61,6 +62,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -87,6 +89,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -33,6 +33,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -12,6 +12,7 @@ jobs:
|
||||
upload:
|
||||
name: Build and upload token config
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
|
||||
permissions:
|
||||
contents: "read"
|
||||
@@ -22,6 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
|
||||
@@ -12,6 +12,7 @@ jobs:
|
||||
upload:
|
||||
name: Upload fonts to CDN
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -20,6 +21,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: "actions/checkout@v7"
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Upload fonts to CDN
|
||||
id: upload
|
||||
|
||||
@@ -15,15 +15,14 @@ jobs:
|
||||
name: Build and deploy to dev-gcp
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: "write"
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
deployments: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup and build Next.js application
|
||||
uses: ./.github/actions/build-website
|
||||
@@ -46,7 +45,7 @@ jobs:
|
||||
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
|
||||
- name: Deploy to dev-gcp
|
||||
uses: nais/deploy/actions/deploy@v2
|
||||
uses: nais/deploy/actions/deploy@v3
|
||||
env:
|
||||
RESOURCE: aksel.nav.no/nais-dev.yaml
|
||||
CLUSTER: dev-gcp
|
||||
|
||||
@@ -26,6 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -48,7 +49,7 @@ jobs:
|
||||
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
|
||||
- name: Deploy to prod-gcp
|
||||
uses: nais/deploy/actions/deploy@v2
|
||||
uses: nais/deploy/actions/deploy@v3
|
||||
env:
|
||||
RESOURCE: apps/mcp-server/.nais/app.yaml
|
||||
CLUSTER: prod-gcp
|
||||
|
||||
@@ -6,6 +6,7 @@ on:
|
||||
- main
|
||||
paths:
|
||||
- "aksel.nav.no/**"
|
||||
- "apps/playroom/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -23,6 +24,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -42,15 +45,14 @@ jobs:
|
||||
name: Build and deploy to prod-gcp
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: "write"
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
deployments: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup and build Next.js application
|
||||
uses: ./.github/actions/build-website
|
||||
@@ -76,7 +78,7 @@ jobs:
|
||||
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
|
||||
- name: Deploy to prod-gcp
|
||||
uses: nais/deploy/actions/deploy@v2
|
||||
uses: nais/deploy/actions/deploy@v3
|
||||
env:
|
||||
RESOURCE: aksel.nav.no/nais-prod.yaml
|
||||
CLUSTER: prod-gcp
|
||||
|
||||
@@ -16,7 +16,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/v7-aksel.nav.no'
|
||||
permissions:
|
||||
packages: "write"
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
steps:
|
||||
@@ -24,6 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
|
||||
- name: Deploy to prod-gcp
|
||||
uses: nais/deploy/actions/deploy@v2
|
||||
uses: nais/deploy/actions/deploy@v3
|
||||
env:
|
||||
RESOURCE: v7-aksel.nav.no/nais-prod.yaml
|
||||
CLUSTER: prod-gcp
|
||||
|
||||
@@ -14,7 +14,8 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build:
|
||||
name: Release packages
|
||||
if: ${{ !contains(github.event.head_commit.message, '[ci skip]') }}
|
||||
# Only publish from main, also when dispatched manually
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: "write"
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 8.19.1
|
||||
|
||||
## 8.19.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Icons: Added new icon MenuHamburgerMagnifyingGlass ([#5368](https://github.com/navikt/aksel/pull/5368))
|
||||
|
||||
## 8.18.0
|
||||
|
||||
## 8.17.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.5 3.25C17.9518 3.25 20.75 6.04822 20.75 9.5C20.75 11.6221 19.6911 13.4953 18.0742 14.625L20.667 19.6562C20.8567 20.0244 20.7119 20.4773 20.3438 20.667C19.9756 20.8567 19.5227 20.7119 19.333 20.3438L16.751 15.3301C16.0524 15.6 15.2938 15.75 14.5 15.75C11.0482 15.75 8.25 12.9518 8.25 9.5C8.25 6.04822 11.0482 3.25 14.5 3.25ZM15.8994 17.25C15.993 17.25 16.0792 17.3026 16.1221 17.3857L16.6377 18.3857C16.723 18.552 16.6021 18.75 16.415 18.75H3.5C3.08579 18.75 2.75 18.4142 2.75 18C2.75 17.5858 3.08579 17.25 3.5 17.25H15.8994ZM14.5 4.75C11.8766 4.75 9.75 6.87665 9.75 9.5C9.75 12.1234 11.8766 14.25 14.5 14.25C17.1234 14.25 19.25 12.1234 19.25 9.5C19.25 6.87665 17.1234 4.75 14.5 4.75ZM6.75195 11.25C6.86836 11.25 6.96896 11.3306 6.99805 11.4434C7.08182 11.7679 7.1863 12.084 7.30957 12.3906C7.37768 12.56 7.2558 12.75 7.07324 12.75H3.5C3.08579 12.75 2.75 12.4142 2.75 12C2.75 11.5858 3.08579 11.25 3.5 11.25H6.75195ZM7.55664 5.25C7.75557 5.25 7.8765 5.47207 7.77734 5.64453C7.60333 5.94718 7.4499 6.26318 7.31738 6.58984C7.27831 6.68596 7.18482 6.75 7.08105 6.75H3.5C3.08579 6.75 2.75 6.41421 2.75 6C2.75 5.58579 3.08579 5.25 3.5 5.25H7.55664Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,12 @@
|
||||
name: MenuHamburgerMagnifyingGlass
|
||||
category: Interface
|
||||
sub_category: Menu
|
||||
keywords:
|
||||
- Menu
|
||||
- meny
|
||||
- hamburger
|
||||
- search
|
||||
- søk
|
||||
- forstørrelsesglass
|
||||
variant: Stroke
|
||||
created_at: 07.10.2026
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "8.17.2",
|
||||
"version": "8.19.1",
|
||||
"author": "Aksel | Nav",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
@@ -75,19 +75,20 @@
|
||||
"@figma/rest-api-spec": "^0.41.0",
|
||||
"@svgr/cli": "8.1.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash": "4.17.25",
|
||||
"@types/react": "19.2.14",
|
||||
"adm-zip": "^0.6.1",
|
||||
"concurrently": "10.0.4",
|
||||
"copyfiles": "^2.4.1",
|
||||
"fast-glob": "3.3.3",
|
||||
"hast-util-select": "6.0.4",
|
||||
"js-yaml": "^5.4.2",
|
||||
"js-yaml": "^5.4.3",
|
||||
"lodash": "4.18.1",
|
||||
"prettier": "3.9.6",
|
||||
"react": "19.2.8",
|
||||
"react": "19.3.0",
|
||||
"rehype-parse": "9.0.1",
|
||||
"tsc-alias": "1.9.1",
|
||||
"tsx": "^4.23.1",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"unified": "11.0.5",
|
||||
"vitest": "4.1.11"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 8.19.1
|
||||
|
||||
## 8.19.0
|
||||
|
||||
## 8.18.0
|
||||
|
||||
## 8.17.2
|
||||
|
||||
## 8.17.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "8.17.2",
|
||||
"version": "8.19.1",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
@@ -43,14 +43,14 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^8.17.2",
|
||||
"@navikt/ds-tokens": "^8.17.2",
|
||||
"@navikt/ds-css": "^8.19.1",
|
||||
"@navikt/ds-tokens": "^8.19.1",
|
||||
"concurrently": "10.0.4",
|
||||
"postcss-selector-parser": "^7.1.6",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"stylelint": "^17.14.1",
|
||||
"stylelint": "^17.15.0",
|
||||
"stylelint-test-rule-node": "^1.2.0",
|
||||
"tsx": "^4.23.1",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,23 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 8.19.1
|
||||
|
||||
## 8.19.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- CLI: Update clipboardy to v5.3.2 ([#5357](https://github.com/navikt/aksel/pull/5357))
|
||||
|
||||
- CLI: Update figlet to 1.11.3 ([#5360](https://github.com/navikt/aksel/pull/5360))
|
||||
|
||||
## 8.18.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Codemod: Migrations now run in folders that are not git repositories, and a missing git install gets its own error message. `codemod v8` no longer stops before `v8-tokens` because of changes made by earlier migrations in the same run. ([`470a820`](https://github.com/navikt/aksel/commit/470a8207f23aee71243b986ef2836967c2498e71))
|
||||
|
||||
- Codemod: `v8-tokens` no longer removes whitespace before replaced tokens or rewrites longer names that contain a token name. ([#5322](https://github.com/navikt/aksel/pull/5322))
|
||||
|
||||
## 8.17.2
|
||||
|
||||
## 8.17.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "8.17.2",
|
||||
"version": "8.19.1",
|
||||
"description": "Aksel command line interface. Codemods and other utilities for Aksel users.",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -42,11 +42,11 @@
|
||||
"dependencies": {
|
||||
"chalk": "5.6.2",
|
||||
"cli-progress": "3.12.0",
|
||||
"clipboardy": "5.3.1",
|
||||
"clipboardy": "5.3.2",
|
||||
"commander": "15.0.0",
|
||||
"enquirer": "2.4.1",
|
||||
"fast-glob": "3.3.3",
|
||||
"figlet": "1.11.0",
|
||||
"figlet": "1.11.3",
|
||||
"is-git-clean": "1.1.0",
|
||||
"jscodeshift": "17.4.0",
|
||||
"lodash": "4.18.1"
|
||||
@@ -55,6 +55,7 @@
|
||||
"@types/cli-progress": "3.11.6",
|
||||
"@types/is-git-clean": "1.1.2",
|
||||
"@types/jscodeshift": "17.3.0",
|
||||
"@types/lodash": "4.17.25",
|
||||
"rimraf": "6.1.3",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.11"
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import type { Command } from "commander";
|
||||
import isGitClean from "is-git-clean";
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { validateGit } from "../validation";
|
||||
|
||||
vi.mock("is-git-clean", () => ({ default: { sync: vi.fn() } }));
|
||||
|
||||
const gitSync = vi.mocked(isGitClean.sync);
|
||||
|
||||
function createProgram() {
|
||||
const error = vi.fn((message: string) => {
|
||||
throw new Error(message);
|
||||
});
|
||||
return { program: { error } as unknown as Command, error };
|
||||
}
|
||||
|
||||
function gitError(props: Record<string, unknown>) {
|
||||
return Object.assign(new Error("Command failed: git status"), props);
|
||||
}
|
||||
|
||||
describe("validateGit", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test("passes when the working tree is clean", () => {
|
||||
gitSync.mockReturnValue(true);
|
||||
const { program, error } = createProgram();
|
||||
|
||||
validateGit({}, program);
|
||||
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("fails when the working tree has changes", () => {
|
||||
gitSync.mockReturnValue(false);
|
||||
const { program, error } = createProgram();
|
||||
|
||||
expect(() => validateGit({}, program)).toThrow(/stash or commit/);
|
||||
expect(error).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test.each([
|
||||
"fatal: not a git repository (or any of the parent directories): .git\n",
|
||||
"fatal: Not a git repository (or any of the parent directories): .git\n",
|
||||
])("passes outside a git repository (%j)", (stderr) => {
|
||||
gitSync.mockImplementation(() => {
|
||||
throw gitError({ status: 128, stderr });
|
||||
});
|
||||
const { program, error } = createProgram();
|
||||
|
||||
validateGit({}, program);
|
||||
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("reports a missing git binary instead of asking to stash", () => {
|
||||
gitSync.mockImplementation(() => {
|
||||
throw gitError({ code: "ENOENT" });
|
||||
});
|
||||
const { program, error } = createProgram();
|
||||
|
||||
expect(() => validateGit({}, program)).toThrow(/git/i);
|
||||
expect(error.mock.calls[0][0]).not.toMatch(/stash or commit/);
|
||||
expect(error.mock.calls[0][0]).toMatch(/--force/);
|
||||
});
|
||||
|
||||
test("fails on other git errors", () => {
|
||||
gitSync.mockImplementation(() => {
|
||||
throw gitError({
|
||||
status: 128,
|
||||
stderr: "fatal: detected dubious ownership",
|
||||
});
|
||||
});
|
||||
const { program } = createProgram();
|
||||
|
||||
expect(() => validateGit({}, program)).toThrow(/stash or commit/);
|
||||
});
|
||||
|
||||
test.each([{ dryRun: true }, { force: true }])(
|
||||
"skips the check with force",
|
||||
(options) => {
|
||||
const { program, error } = createProgram();
|
||||
|
||||
validateGit(options, program);
|
||||
|
||||
expect(gitSync).not.toHaveBeenCalled();
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
});
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/* @ts-expect-error No avaliable types for testUtils */
|
||||
import { applyTransform } from "jscodeshift/dist/testUtils";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import * as transform from "../update-js-tokens";
|
||||
|
||||
const run = (source: string) =>
|
||||
applyTransform({ ...transform, parser: "tsx" }, {}, { source });
|
||||
|
||||
describe("update-js-tokens keeps boundaries intact", () => {
|
||||
test("does not replace inside longer identifiers", () => {
|
||||
expect(
|
||||
run(
|
||||
`import { NavdsGlobalColorGray100 } from "@navikt/ds-tokens/dist/tokens";\n\nconst a = [NavdsGlobalColorGray100, MyNavdsGlobalColorGray100, NavdsGlobalColorGray1000];`,
|
||||
),
|
||||
).toBe(
|
||||
`import { AGray100 } from "@navikt/ds-tokens/dist/tokens";\n\nconst a = [AGray100, MyNavdsGlobalColorGray100, NavdsGlobalColorGray1000];`,
|
||||
);
|
||||
});
|
||||
|
||||
test("does not replace similar names when aliased", () => {
|
||||
expect(
|
||||
run(
|
||||
`import { NavdsGlobalColorGray100 as gray } from "@navikt/ds-tokens/dist/tokens";\n\nconst a = [gray, grayish, mygray];`,
|
||||
),
|
||||
).toBe(
|
||||
`import { AGray100 } from "@navikt/ds-tokens/dist/tokens";\n\nconst a = [AGray100, grayish, mygray];`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { API, JSCodeshift } from "jscodeshift";
|
||||
import { tokens } from "../../../tokens-map.js";
|
||||
import { createIdentifierRegex } from "../../../utils/identifier-regex";
|
||||
import {
|
||||
getImportSpecifier,
|
||||
getImportSpecifierName,
|
||||
@@ -60,8 +61,7 @@ export default function transformer(file: JSCodeshift, api: API) {
|
||||
|
||||
let code = root.toSource(getLineTerminator(file.source));
|
||||
|
||||
const rgx = new RegExp(`(${localName})`, "gm");
|
||||
code = code.replace(rgx, out);
|
||||
code = code.replace(createIdentifierRegex(String(localName)), out);
|
||||
root = j(code);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { applyTransform } from "jscodeshift/dist/testUtils";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
// eslint-disable-next-line import/default
|
||||
import prettier from "prettier";
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Matches a JS identifier as a whole word.
|
||||
* Lookarounds are zero-width, so replacing the match never removes surrounding whitespace,
|
||||
* and longer identifiers containing the name (`MyToken`, `Token2`, `$Token`) are left untouched.
|
||||
*/
|
||||
function createIdentifierRegex(identifier: string) {
|
||||
const escaped = identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
return new RegExp(`(?<![\\w$])${escaped}(?![\\w$])`, "gm");
|
||||
}
|
||||
|
||||
export { createIdentifierRegex };
|
||||
@@ -218,8 +218,25 @@ describe("getTokenRegex JS", () => {
|
||||
|
||||
expect("ATextRed200").not.toMatch(regex);
|
||||
expect("AATextRed200").not.toMatch(regex);
|
||||
expect("AATextRed").not.toMatch(regex);
|
||||
expect("$ATextRed").not.toMatch(regex);
|
||||
});
|
||||
|
||||
/* Since we cant do a positive lookbehind, these cases will be matched */
|
||||
expect("AATextRed").toMatch(regex);
|
||||
test("should not include surrounding whitespace in the match", () => {
|
||||
const regex = getTokenRegex("--a-text-red", "js");
|
||||
|
||||
expect(" ATextRed ".match(regex)).toEqual(["ATextRed"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getTokenRegex does not consume leading whitespace", () => {
|
||||
test.each([
|
||||
["css", " var(--a-text-red)", "--a-text-red"],
|
||||
["scss", "margin: 0 $a-text-red", "$a-text-red"],
|
||||
["less", "margin: 0 @a-text-red", "@a-text-red"],
|
||||
] as const)("%s", (format, input, expected) => {
|
||||
expect(input.match(getTokenRegex("--a-text-red", format))).toEqual([
|
||||
expected,
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -39,26 +39,29 @@ function getFrameworkRegexes({
|
||||
return regexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms replace the full match. We use lookarounds so that the regex does not consume surrounding characters.
|
||||
*/
|
||||
function getTokenRegex(
|
||||
variable: string,
|
||||
format: "css" | "scss" | "less" | "js",
|
||||
) {
|
||||
switch (format) {
|
||||
case "css":
|
||||
return new RegExp(`(\\s|^)?(${variable})(?=\\s|$|[^\\w-])`, "gm");
|
||||
return new RegExp(`(?<![\\w-])(${variable})(?![\\w-])`, "gm");
|
||||
case "scss":
|
||||
return new RegExp(
|
||||
`(\\s|^)?(\\${translateToken(variable, "scss")})(?=\\s|$|[^\\w-])`,
|
||||
`(?<![\\w-])(\\${translateToken(variable, "scss")})(?![\\w-])`,
|
||||
"gm",
|
||||
);
|
||||
case "less":
|
||||
return new RegExp(
|
||||
`(\\s|^)?(${translateToken(variable, "less")})(?=\\s|$|[^\\w-])`,
|
||||
`(?<![\\w-])(${translateToken(variable, "less")})(?![\\w-])`,
|
||||
"gm",
|
||||
);
|
||||
case "js":
|
||||
return new RegExp(
|
||||
`(\\s|^)?(${translateToken(variable, "js")})(?=\\s|$|[^\\w-])`,
|
||||
`(?<![\\w$])(${translateToken(variable, "js")})(?![\\w$])`,
|
||||
"gm",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { runTooling } from "./run-tooling.js";
|
||||
|
||||
const program = new Command();
|
||||
|
||||
function v8TokensCommand() {
|
||||
function v8TokensCommand({ skipGitCheck = false } = {}) {
|
||||
program.allowExcessArguments();
|
||||
program.name(`${chalk.blueBright(`npx @navikt/aksel v8-tokens`)}`);
|
||||
|
||||
@@ -24,7 +24,10 @@ function v8TokensCommand() {
|
||||
program.parse();
|
||||
const options = program.opts();
|
||||
|
||||
runTooling(options as Parameters<typeof runTooling>["0"], program);
|
||||
return runTooling(
|
||||
{ ...(options as Parameters<typeof runTooling>["0"]), skipGitCheck },
|
||||
program,
|
||||
);
|
||||
}
|
||||
|
||||
export { v8TokensCommand };
|
||||
@@ -31,6 +31,11 @@ type ToolingOptions = {
|
||||
dryRun: boolean;
|
||||
glob: string;
|
||||
ext: string;
|
||||
/**
|
||||
* Set when the caller has already validated git (e.g. `codemod v8`),
|
||||
* since earlier migrations in the same run leave the tree dirty.
|
||||
*/
|
||||
skipGitCheck?: boolean;
|
||||
};
|
||||
|
||||
type CodeshiftOptions = {
|
||||
@@ -90,6 +95,15 @@ export async function runTooling(
|
||||
// Show initial status
|
||||
const initialStatus = getStatus(filepaths);
|
||||
|
||||
// Only check git before the first write: later tasks see our own changes.
|
||||
let gitChecked = !!options.skipGitCheck;
|
||||
const ensureGitChecked = () => {
|
||||
if (!gitChecked) {
|
||||
validateGit(options, program);
|
||||
gitChecked = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Task execution loop
|
||||
let task: TaskName = await getNextTask(initialStatus.status);
|
||||
let currentStatus = initialStatus;
|
||||
@@ -105,6 +119,7 @@ export async function runTooling(
|
||||
program,
|
||||
currentStatus,
|
||||
() => getStatus(filepaths, "no-print"),
|
||||
ensureGitChecked,
|
||||
);
|
||||
} catch (error) {
|
||||
program.error(
|
||||
@@ -117,8 +132,6 @@ export async function runTooling(
|
||||
|
||||
task = await getNextTask(currentStatus.status);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +144,7 @@ async function executeTask(
|
||||
program: Command,
|
||||
statusStore: TokenStatus,
|
||||
updateStatus: () => TokenStatus,
|
||||
ensureGitChecked: () => void,
|
||||
): Promise<TokenStatus> {
|
||||
switch (task) {
|
||||
case "status":
|
||||
@@ -147,9 +161,7 @@ async function executeTask(
|
||||
case "less-tokens":
|
||||
case "js-tokens":
|
||||
case "tailwind-tokens": {
|
||||
if (!options.force) {
|
||||
validateGit(options, program);
|
||||
}
|
||||
ensureGitChecked();
|
||||
const scopedFiles = getScopedFilesForTask(
|
||||
task,
|
||||
filepaths,
|
||||
@@ -179,9 +191,7 @@ async function executeTask(
|
||||
"tailwind-tokens",
|
||||
] as const;
|
||||
|
||||
if (!options.force) {
|
||||
validateGit(options, program);
|
||||
}
|
||||
ensureGitChecked();
|
||||
|
||||
let currentStatus = statusStore;
|
||||
const summaryData: {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import type { Command } from "commander";
|
||||
import Enquirer from "enquirer";
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { validateGit } from "../../validation";
|
||||
import { TokenStatus } from "../config/TokenStatus";
|
||||
import { runTooling } from "../run-tooling";
|
||||
|
||||
vi.mock("enquirer", () => ({ default: { prompt: vi.fn() } }));
|
||||
vi.mock("fast-glob", () => ({ default: vi.fn(async () => []) }));
|
||||
vi.mock("jscodeshift/src/Runner", () => ({
|
||||
run: vi.fn(async () => ({ error: 0, ok: 0, nochange: 0, skip: 0 })),
|
||||
}));
|
||||
vi.mock("../../validation", () => ({ validateGit: vi.fn() }));
|
||||
vi.mock("../tasks/status", () => ({
|
||||
getStatus: vi.fn(() => new TokenStatus()),
|
||||
}));
|
||||
vi.mock("../tasks/print-remaining", () => ({ printRemaining: vi.fn() }));
|
||||
|
||||
const prompt = vi.mocked(Enquirer.prompt);
|
||||
const program = { error: vi.fn() } as unknown as Command;
|
||||
const baseOptions = { force: false, dryRun: false, glob: "**/*.css", ext: "" };
|
||||
|
||||
function selectTasks(...tasks: string[]) {
|
||||
for (const task of [...tasks, "exit"]) {
|
||||
prompt.mockResolvedValueOnce({ task });
|
||||
}
|
||||
}
|
||||
|
||||
describe("runTooling", () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(console, "info").mockImplementation(() => {});
|
||||
vi.spyOn(process, "exit").mockImplementation((code) => {
|
||||
throw new Error(`process.exit(${code})`);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test("returns instead of exiting the process when the user exits", async () => {
|
||||
selectTasks();
|
||||
|
||||
await expect(runTooling(baseOptions, program)).resolves.toBeUndefined();
|
||||
expect(process.exit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("checks git once, even when several migrations run in a row", async () => {
|
||||
selectTasks("css-tokens", "scss-tokens", "run-all-migrations");
|
||||
|
||||
await runTooling(baseOptions, program);
|
||||
|
||||
expect(validateGit).toHaveBeenCalledTimes(1);
|
||||
expect(program.error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("skips the git check when the caller already did it", async () => {
|
||||
selectTasks("css-tokens", "run-all-migrations");
|
||||
|
||||
await runTooling({ ...baseOptions, skipGitCheck: true }, program);
|
||||
|
||||
expect(validateGit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("does not check git for read-only tasks", async () => {
|
||||
selectTasks("status", "print-remaining-tokens");
|
||||
|
||||
await runTooling(baseOptions, program);
|
||||
|
||||
expect(validateGit).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
/* @ts-expect-error No avaliable types for testUtils */
|
||||
import { applyTransform } from "jscodeshift/dist/testUtils";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import * as jsTransform from "../v8-tokens-js";
|
||||
import lessTransform from "../v8-tokens-less";
|
||||
import scssTransform from "../v8-tokens-scss";
|
||||
|
||||
/*
|
||||
* Fixture tests are compared after Prettier, which hides whitespace bugs.
|
||||
* These tests compare the exact output.
|
||||
*/
|
||||
const runJs = (source: string) =>
|
||||
applyTransform({ ...jsTransform, parser: "tsx" }, {}, { source });
|
||||
|
||||
const runStyle = (
|
||||
transformer: typeof scssTransform | typeof lessTransform,
|
||||
source: string,
|
||||
) => transformer({ path: "test", source });
|
||||
|
||||
describe("v8-tokens-js keeps boundaries intact", () => {
|
||||
const importLine = `import { ASurfaceDefault } from "@navikt/ds-tokens/dist/tokens";\n`;
|
||||
const expectedImport = `import { BgDefault } from "@navikt/ds-tokens/js";\n`;
|
||||
|
||||
test.each([
|
||||
["return ASurfaceDefault;", "return BgDefault;"],
|
||||
["const a = ASurfaceDefault;", "const a = BgDefault;"],
|
||||
["const a = [ASurfaceDefault];", "const a = [BgDefault];"],
|
||||
["const a = {...ASurfaceDefault};", "const a = {...BgDefault};"],
|
||||
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing token inside template literal
|
||||
["const a = `${ASurfaceDefault}`;", "const a = `${BgDefault}`;"],
|
||||
])("%s", (input, output) => {
|
||||
expect(runJs(`${importLine}\n${input}`)).toBe(
|
||||
`${expectedImport}\n${output}`,
|
||||
);
|
||||
});
|
||||
|
||||
test("does not replace inside longer identifiers", () => {
|
||||
expect(
|
||||
runJs(
|
||||
`${importLine}\nconst a = [MyASurfaceDefault, ASurfaceDefault2, $ASurfaceDefault];`,
|
||||
),
|
||||
).toBe(
|
||||
`${expectedImport}\nconst a = [MyASurfaceDefault, ASurfaceDefault2, $ASurfaceDefault];`,
|
||||
);
|
||||
});
|
||||
|
||||
test("replaces aliased imports without touching similar names", () => {
|
||||
expect(
|
||||
runJs(
|
||||
`import { ASurfaceDefault as surf } from "@navikt/ds-tokens/dist/tokens";\n\nconst a = [surf, surface, mysurf];`,
|
||||
),
|
||||
).toBe(`${expectedImport}\nconst a = [BgDefault, surface, mysurf];`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("v8-tokens-scss keeps whitespace", () => {
|
||||
test.each([
|
||||
["margin: 0 $a-spacing-4;", "margin: 0 $ax-space-16;"],
|
||||
["color: $a-text-default;", "color: $ax-text-neutral;"],
|
||||
["color:$a-text-default;", "color:$ax-text-neutral;"],
|
||||
["$a-text-default", "$ax-text-neutral"],
|
||||
["\n\t$a-text-default", "\n\t$ax-text-neutral"],
|
||||
["color: common.$a-text-default;", "color: common.$ax-text-neutral;"],
|
||||
[
|
||||
"padding: $a-spacing-4 $a-spacing-4;",
|
||||
"padding: $ax-space-16 $ax-space-16;",
|
||||
],
|
||||
])("%s", (input, output) => {
|
||||
expect(runStyle(scssTransform, input)).toBe(output);
|
||||
});
|
||||
});
|
||||
|
||||
describe("v8-tokens-less keeps whitespace", () => {
|
||||
test.each([
|
||||
["margin: 0 @a-spacing-4;", "margin: 0 @ax-space-16;"],
|
||||
["color: @a-text-default;", "color: @ax-text-neutral;"],
|
||||
["color:@a-text-default;", "color:@ax-text-neutral;"],
|
||||
[
|
||||
"padding: @a-spacing-4 @a-spacing-4;",
|
||||
"padding: @ax-space-16 @ax-space-16;",
|
||||
],
|
||||
])("%s", (input, output) => {
|
||||
expect(runStyle(lessTransform, input)).toBe(output);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { API, FileInfo } from "jscodeshift";
|
||||
import { createIdentifierRegex } from "../../utils/identifier-regex";
|
||||
import { getImportSpecifier } from "../../utils/imports";
|
||||
import { getLineTerminator } from "../../utils/lineterminator";
|
||||
import moveAndRenameImport from "../../utils/packageImports";
|
||||
@@ -63,8 +64,9 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
|
||||
let code = root.toSource(getLineTerminator(src));
|
||||
|
||||
const rgx = new RegExp(`(\\s|^)?(${localName})(?=\\s|$|[^\\w-])`, "gm");
|
||||
code = code.replace(rgx, jsToken);
|
||||
if (localName) {
|
||||
code = code.replace(createIdentifierRegex(localName), jsToken);
|
||||
}
|
||||
src = code;
|
||||
|
||||
root = j(code);
|
||||
|
||||
@@ -43,7 +43,14 @@ export function validateGit(options: any, program: Command) {
|
||||
try {
|
||||
clean = isGitClean.sync(process.cwd());
|
||||
} catch (err: any) {
|
||||
if (err?.stderr && err.stderr.indexOf("Not a git repository") >= 0) {
|
||||
if (err?.code === "ENOENT") {
|
||||
program.error(
|
||||
`${chalk.yellow(
|
||||
"\nCould not find git, so we can't check for uncommitted changes.",
|
||||
)}${"\nInstall git, or use the --force flag to override this safety check."}`,
|
||||
);
|
||||
}
|
||||
if (/not a git repository/i.test(String(err?.stderr ?? ""))) {
|
||||
clean = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,14 @@ async function run() {
|
||||
|
||||
if (args[0] === "codemod") {
|
||||
if (args.includes("v8-tokens")) {
|
||||
v8TokensCommand();
|
||||
return;
|
||||
await v8TokensCommand();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
codemodCommand((migration) => {
|
||||
codemodCommand(async (migration) => {
|
||||
if (migration === "v8-tokens") {
|
||||
v8TokensCommand();
|
||||
/* `codemod v8` has already checked git before running earlier migrations */
|
||||
await v8TokensCommand({ skipGitCheck: true });
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 8.19.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Accordion: `@media print` now auto-opens every Accordion ([#5363](https://github.com/navikt/aksel/pull/5363))
|
||||
|
||||
- ExpansionCard: `@media print` now auto-opens every ExpansionCard ([#5363](https://github.com/navikt/aksel/pull/5363))
|
||||
|
||||
- ReadMore: `@media print` now auto-opens every ReadMore ([#5363](https://github.com/navikt/aksel/pull/5363))
|
||||
|
||||
## 8.19.0
|
||||
|
||||
## 8.18.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ActionMenu: Faster and smoother open transition. ([#5296](https://github.com/navikt/aksel/pull/5296))
|
||||
|
||||
## 8.17.2
|
||||
|
||||
## 8.17.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "8.17.2",
|
||||
"version": "8.19.1",
|
||||
"description": "CSS for Nav Designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -32,12 +32,12 @@
|
||||
"css:get-version": "node config/get-version.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^8.17.2",
|
||||
"@navikt/ds-tokens": "^8.19.1",
|
||||
"browserslist": "^4.28.7",
|
||||
"esbuild": "^0.28.2",
|
||||
"fast-glob": "3.3.3",
|
||||
"lightningcss": "^1.33.0",
|
||||
"tsx": "^4.23.1"
|
||||
"tsx": "^4.23.15"
|
||||
},
|
||||
"browserslist": [
|
||||
">= 0.2% in NO",
|
||||
|
||||
@@ -123,6 +123,11 @@
|
||||
|
||||
.aksel-accordion--indent > .aksel-accordion__item > .aksel-accordion__content {
|
||||
box-shadow: -2px 0 0 0 var(--ax-border-neutral-subtleA);
|
||||
|
||||
@media print {
|
||||
border-left: 1px solid black;
|
||||
margin-left: calc(var(--ax-space-24) - 2px);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-accordion__item {
|
||||
@@ -163,4 +168,16 @@
|
||||
border-bottom: 1px solid var(--ax-border-neutral-subtleA);
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
& > .aksel-accordion__content {
|
||||
visibility: visible;
|
||||
|
||||
/* Override grid layout for print. Grid causes element to stretch unexpectedly across printed pages in FF */
|
||||
display: block;
|
||||
margin-block: var(--ax-space-8);
|
||||
margin-block-end: var(--ax-space-24);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,13 @@
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
transform-origin: var(--__axc-action-menu-content-transform-origin);
|
||||
transition:
|
||||
transform 150ms ease allow-discrete,
|
||||
opacity 150ms ease allow-discrete;
|
||||
transform 100ms cubic-bezier(0.25, 0.46, 0.45, 0.94) allow-discrete,
|
||||
opacity 100ms cubic-bezier(0.25, 0.46, 0.45, 0.94) allow-discrete;
|
||||
|
||||
&[data-state="open"] {
|
||||
@starting-style {
|
||||
opacity: 0.8;
|
||||
transform: scale3d(0.94, 0.94, 0.94);
|
||||
opacity: 0.1;
|
||||
transform: scale3d(0.95, 0.95, 0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
box-shadow: 0 0 0 1px var(--ax-border-default);
|
||||
border-color: var(--ax-border-default);
|
||||
}
|
||||
|
||||
@media print {
|
||||
border-color: black;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------ ExpansionCard Small-size ------------------------ */
|
||||
@@ -166,6 +170,17 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
/* Override grid layout for print. Grid causes element to stretch unexpectedly across printed pages in FF */
|
||||
display: block;
|
||||
visibility: visible;
|
||||
padding-block: var(--__axc-expansioncard-padding-block);
|
||||
|
||||
& .aksel-expansioncard__content-inner {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-expansioncard__content-inner {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
color: var(--ax-text-neutral);
|
||||
width: 100%;
|
||||
min-height: 3rem;
|
||||
display: inline-block;
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: var(--ax-space-8);
|
||||
padding-right: var(--ax-space-40);
|
||||
@@ -81,10 +81,11 @@
|
||||
|
||||
.aksel-combobox2__field-chevron {
|
||||
position: absolute;
|
||||
font-size: 1.5rem;
|
||||
right: var(--ax-space-8);
|
||||
top: 0;
|
||||
margin-top: 0.75rem; /* We don't use dynamic centering because we don't want it to move if height changes */
|
||||
font-size: 1.5rem;
|
||||
pointer-events: none;
|
||||
margin-top: 12px; /* We don't use dynamic centering because we don't want it to move if height changes */
|
||||
color: var(--ax-text-neutral);
|
||||
|
||||
@media (forced-colors: active) {
|
||||
@@ -92,7 +93,7 @@
|
||||
}
|
||||
|
||||
.aksel-combobox2[data-size="small"] & {
|
||||
margin-top: 4px;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +237,12 @@
|
||||
.aksel-combobox2__option:is([aria-selected="true"], [aria-selected="true"][data-virtual-focus="true"])
|
||||
.aksel-listbox__option-inner {
|
||||
background-color: var(--ax-bg-moderate-pressedA);
|
||||
|
||||
@media (forced-colors: active) {
|
||||
background-color: SelectedItem;
|
||||
color: SelectedItemText;
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-combobox2__option-checkmark {
|
||||
|
||||
@@ -19,6 +19,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Radio card */
|
||||
.aksel-radio-card {
|
||||
width: 100%;
|
||||
align-content: flex-start;
|
||||
padding: var(--ax-space-16) var(--ax-space-20);
|
||||
cursor: pointer;
|
||||
background-color: var(--ax-bg-default);
|
||||
border: 1px solid var(--ax-border-neutral-subtleA);
|
||||
border-radius: var(--ax-radius-8);
|
||||
|
||||
&:focus-within::after,
|
||||
&.aksel-radio--small:focus-within::after {
|
||||
inset: 0;
|
||||
border-radius: calc(var(--ax-radius-8) - 1px);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Radio card group */
|
||||
.aksel-radio-card-group {
|
||||
& > .aksel-radio-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ax-space-12);
|
||||
|
||||
> .aksel-hgrid:not(.aksel-hgrid-gap) {
|
||||
gap: var(--ax-space-12);
|
||||
}
|
||||
}
|
||||
|
||||
&.aksel-radio-group--small > .aksel-radio-buttons > .aksel-hgrid:not(.aksel-hgrid-gap) {
|
||||
gap: var(--ax-space-8);
|
||||
}
|
||||
}
|
||||
|
||||
/* Small */
|
||||
.aksel-checkbox--small,
|
||||
.aksel-radio--small {
|
||||
@@ -30,6 +65,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-radio-card.aksel-radio--small {
|
||||
padding: var(--ax-space-12) var(--ax-space-16);
|
||||
}
|
||||
|
||||
.aksel-radio-card-group.aksel-radio-group--small > .aksel-radio-buttons {
|
||||
gap: var(--ax-space-8);
|
||||
}
|
||||
|
||||
/* Label */
|
||||
.aksel-checkbox__label,
|
||||
.aksel-radio__label {
|
||||
@@ -203,9 +246,27 @@
|
||||
--__axc-radio-checkbox-marker-border: 7px;
|
||||
}
|
||||
|
||||
/* Radio card border in hover and checked states */
|
||||
.aksel-radio-card:is(
|
||||
:hover:not(:where(.aksel-radio--disabled, .aksel-radio--readonly, .aksel-radio--error)),
|
||||
:has(> .aksel-radio__input:checked):not(:where(.aksel-radio--disabled, .aksel-radio--readonly))
|
||||
) {
|
||||
border-color: var(--ax-border-default);
|
||||
box-shadow: 0 0 0 1px var(--ax-border-default);
|
||||
}
|
||||
|
||||
/* Radio card background in checked state */
|
||||
.aksel-radio-card:has(> .aksel-radio__input:checked):not(
|
||||
:where(.aksel-radio--disabled, .aksel-radio--readonly, .aksel-radio--error)
|
||||
) {
|
||||
background-color: var(--ax-bg-accent-moderateA);
|
||||
}
|
||||
|
||||
/* Marker colors in unchecked hover state (unless disabled) */
|
||||
.aksel-checkbox__input:hover:not(:disabled, :checked, :indeterminate),
|
||||
.aksel-radio__input:hover:not(:disabled, :checked) {
|
||||
.aksel-radio__input:hover:not(:disabled, :checked),
|
||||
.aksel-radio-card:hover:not(:where(.aksel-radio--disabled, .aksel-radio--readonly))
|
||||
> .aksel-radio__input:not(:disabled, :checked) {
|
||||
border-color: var(--ax-border-strong);
|
||||
background-color: var(--ax-bg-moderate-hoverA);
|
||||
}
|
||||
@@ -222,15 +283,26 @@
|
||||
opacity: var(--ax-opacity-disabled);
|
||||
}
|
||||
|
||||
/* Radio card background in disabled and readonly states */
|
||||
.aksel-radio-card:where(.aksel-radio--readonly) {
|
||||
background-color: var(--ax-bg-neutral-moderate);
|
||||
}
|
||||
|
||||
.aksel-radio-card.aksel-radio--disabled > .aksel-radio__input:checked {
|
||||
border-color: var(--ax-border-neutral-strong);
|
||||
}
|
||||
|
||||
/* Cursor in disabled state on otherwise clickable elements */
|
||||
.aksel-checkbox--disabled :where(.aksel-checkbox__input, .aksel-checkbox__label),
|
||||
.aksel-radio--disabled > :where(.aksel-radio__input, .aksel-radio__label) {
|
||||
.aksel-radio--disabled > :where(.aksel-radio__input, .aksel-radio__label),
|
||||
.aksel-radio-card.aksel-radio--disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Cursor in readonly state on otherwise clickable elements */
|
||||
.aksel-checkbox--readonly :where(.aksel-checkbox__input, .aksel-checkbox__label),
|
||||
.aksel-radio--readonly > :where(.aksel-radio__input, .aksel-radio__label) {
|
||||
.aksel-radio--readonly > :where(.aksel-radio__input, .aksel-radio__label),
|
||||
.aksel-radio-card.aksel-radio--readonly {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,10 @@
|
||||
pointer-events: none;
|
||||
font-size: 1.5rem;
|
||||
|
||||
@media (forced-colors: active) {
|
||||
color: CanvasText;
|
||||
}
|
||||
|
||||
.aksel-search--disabled & {
|
||||
opacity: var(--ax-opacity-disabled);
|
||||
}
|
||||
|
||||
@@ -49,5 +49,9 @@
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
outline-offset: -2px;
|
||||
background-color: var(--ax-bg-neutral-moderate-hoverA);
|
||||
|
||||
@media (forced-colors: active) {
|
||||
outline-color: Highlight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,10 @@
|
||||
|
||||
.aksel-read-more[data-state="closed"] & {
|
||||
display: none;
|
||||
|
||||
@media print {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 8.19.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.19.1
|
||||
- @navikt/aksel-icons@8.19.1
|
||||
|
||||
## 8.19.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`c5dd309`](https://github.com/navikt/aksel/commit/c5dd3097e51ef2a9f79964248398bd158660bd3b)]:
|
||||
- @navikt/aksel-icons@8.19.0
|
||||
- @navikt/ds-tokens@8.19.0
|
||||
|
||||
## 8.18.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Radio: Add RadioCard and RadioCardGroup components ([#5276](https://github.com/navikt/aksel/pull/5276))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Badge, Lookup: now available outside of Preview scope ([#5344](https://github.com/navikt/aksel/pull/5344))
|
||||
|
||||
- DatePicker: Use without hook now correctly closes picker on date-selection ([#5327](https://github.com/navikt/aksel/pull/5327))
|
||||
|
||||
- ActionMenu: Menu is no longer modal, Page scroll and interaction outside the menu are no longer blocked while open. ([#5296](https://github.com/navikt/aksel/pull/5296))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.18.0
|
||||
- @navikt/aksel-icons@8.18.0
|
||||
|
||||
## 8.17.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "8.17.2",
|
||||
"version": "8.19.1",
|
||||
"description": "React components from the Norwegian Labour and Welfare Administration.",
|
||||
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
|
||||
"license": "MIT",
|
||||
@@ -86,6 +86,16 @@
|
||||
"default": "./cjs/badge/index.js"
|
||||
}
|
||||
},
|
||||
"./PREVIEW/RadioCard": {
|
||||
"import": {
|
||||
"types": "./esm/form/radio-card/index.d.ts",
|
||||
"default": "./esm/form/radio-card/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./cjs/form/radio-card/index.d.ts",
|
||||
"default": "./cjs/form/radio-card/index.js"
|
||||
}
|
||||
},
|
||||
"./types/theme": {
|
||||
"types": "./esm/types/theme.d.ts"
|
||||
},
|
||||
@@ -729,6 +739,26 @@
|
||||
"default": "./cjs/dialog/index.js"
|
||||
}
|
||||
},
|
||||
"./Lookup": {
|
||||
"import": {
|
||||
"types": "./esm/lookup/index.d.ts",
|
||||
"default": "./esm/lookup/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./cjs/lookup/index.d.ts",
|
||||
"default": "./cjs/lookup/index.js"
|
||||
}
|
||||
},
|
||||
"./Badge": {
|
||||
"import": {
|
||||
"types": "./esm/badge/index.d.ts",
|
||||
"default": "./esm/badge/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./cjs/badge/index.d.ts",
|
||||
"default": "./cjs/badge/index.js"
|
||||
}
|
||||
},
|
||||
"./package.json": "./package.json",
|
||||
"./Theme": {
|
||||
"import": {
|
||||
@@ -758,8 +788,8 @@
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "0.27.20",
|
||||
"@floating-ui/react-dom": "^2.1.9",
|
||||
"@navikt/aksel-icons": "^8.17.2",
|
||||
"@navikt/ds-tokens": "^8.17.2",
|
||||
"@navikt/aksel-icons": "^8.19.1",
|
||||
"@navikt/ds-tokens": "^8.19.1",
|
||||
"date-fns": "^4.0.0",
|
||||
"react-day-picker": "10.0.1"
|
||||
},
|
||||
@@ -767,23 +797,22 @@
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.3",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@testing-library/user-event": "^14.6.7",
|
||||
"@types/jscodeshift": "17.3.0",
|
||||
"@types/node": "^24.10.9",
|
||||
"@types/react": "19.2.14",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"aksel": "workspace:^",
|
||||
"concurrently": "10.0.4",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"jscodeshift": "17.4.0",
|
||||
"jsdom": "29.1.1",
|
||||
"react": "19.2.8",
|
||||
"react-dom": "19.2.8",
|
||||
"react": "19.3.0",
|
||||
"react-dom": "19.3.0",
|
||||
"rimraf": "6.1.3",
|
||||
"swr": "^2.4.2",
|
||||
"tsc-alias": "1.9.1",
|
||||
"tsx": "^4.23.1",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { type ParsedMeta, parseMetaFiles } from "./parse-meta";
|
||||
import { type ParsedMeta, readMetaFiles } from "./parse-meta";
|
||||
import { validateMetas } from "./validate-meta";
|
||||
|
||||
describe("component metadata files", () => {
|
||||
test("all meta files satisfy the metadata spec", () => {
|
||||
const metas = parseMetaFiles();
|
||||
const metas = readMetaFiles();
|
||||
expect(validateMetas(metas)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import fg from "fast-glob";
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import ts from "typescript";
|
||||
import { META_GLOB, packageRoot, tsconfigPath } from "./paths";
|
||||
@@ -34,6 +35,21 @@ interface ParsedMeta {
|
||||
utils: ResolvedEntry[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax-only contents of one `*.meta.ts` file. `components`/`utils` hold the
|
||||
* map labels, e.g. `"Accordion.Item"`, without resolving their declarations.
|
||||
*/
|
||||
interface MetaSummary extends Omit<ParsedMeta, "components" | "utils"> {
|
||||
components: string[];
|
||||
utils: string[];
|
||||
}
|
||||
|
||||
/** Syntactic parts of a meta file, shared by the summary and full parsers. */
|
||||
interface MetaSource extends Omit<ParsedMeta, "components" | "utils"> {
|
||||
componentsMap: ts.ObjectLiteralExpression;
|
||||
utilsMap: ts.ObjectLiteralExpression | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts exported "metadata" object through AST
|
||||
*/
|
||||
@@ -146,14 +162,122 @@ function createTsProgram(metaFiles: string[]): ts.Program {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display label of a `components`/`utils` map entry, or
|
||||
* `undefined` for entries that are neither `Name` nor `"Label": Name`.
|
||||
*/
|
||||
function getEntryLabel(entry: ts.ObjectLiteralElementLike) {
|
||||
if (ts.isShorthandPropertyAssignment(entry)) {
|
||||
return entry.name.text;
|
||||
}
|
||||
if (ts.isPropertyAssignment(entry) && ts.isIdentifier(entry.initializer)) {
|
||||
return ts.isStringLiteral(entry.name)
|
||||
? entry.name.text
|
||||
: entry.name.getText();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a `components`/`utils` map from the metadata object. Throws when the
|
||||
* property exists but is not an object literal.
|
||||
*/
|
||||
function readEntryMap(
|
||||
metadataNode: ts.ObjectLiteralExpression,
|
||||
key: "components" | "utils",
|
||||
relMeta: string,
|
||||
) {
|
||||
const prop = getObjectProperty(metadataNode, key);
|
||||
if (!prop) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
!ts.isPropertyAssignment(prop) ||
|
||||
!ts.isObjectLiteralExpression(prop.initializer)
|
||||
) {
|
||||
throw new Error(`[${relMeta}] metadata.${key} must be an object literal.`);
|
||||
}
|
||||
return prop.initializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the syntactic parts of a meta file. Needs no type information, so it
|
||||
* works on a standalone `ts.SourceFile`.
|
||||
*/
|
||||
function readMetaSource(
|
||||
sourceFile: ts.SourceFile,
|
||||
metaFile: string,
|
||||
): MetaSource {
|
||||
const relMeta = path.relative(packageRoot, metaFile);
|
||||
|
||||
const metadataNode = findMetadataObject(sourceFile);
|
||||
if (!metadataNode) {
|
||||
throw new Error(`[${relMeta}] Could not find a "metadata" object.`);
|
||||
}
|
||||
|
||||
const name = readStringLiteralProperty(metadataNode, "name");
|
||||
if (!name) {
|
||||
throw new Error(`[${relMeta}] metadata.name must be a string literal.`);
|
||||
}
|
||||
|
||||
const componentsMap = readEntryMap(metadataNode, "components", relMeta);
|
||||
if (!componentsMap) {
|
||||
throw new Error(
|
||||
`[${relMeta}] metadata.components must be an object literal.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
dir: path.relative(packageRoot, path.dirname(metaFile)),
|
||||
metaFile: relMeta,
|
||||
keywords: readStringArrayProperty(metadataNode, "keywords"),
|
||||
related: readStringArrayProperty(metadataNode, "related"),
|
||||
componentsMap,
|
||||
utilsMap: readEntryMap(metadataNode, "utils", relMeta),
|
||||
};
|
||||
}
|
||||
|
||||
function findMetaFiles() {
|
||||
return fg.sync(META_GLOB, { cwd: packageRoot, absolute: true }).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads every `*.meta.ts` file under the package without building a
|
||||
* TypeScript program. Much faster than {@link parseMetaFiles}, but entries are
|
||||
* not resolved to declarations. Use for spec validation only.
|
||||
*/
|
||||
function readMetaFiles(): MetaSummary[] {
|
||||
const getLabels = (map: ts.ObjectLiteralExpression) =>
|
||||
map.properties
|
||||
.map(getEntryLabel)
|
||||
.filter((label): label is string => Boolean(label));
|
||||
|
||||
return findMetaFiles().map((metaFile) => {
|
||||
const sourceFile = ts.createSourceFile(
|
||||
metaFile,
|
||||
readFileSync(metaFile, "utf8"),
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
);
|
||||
const { componentsMap, utilsMap, ...meta } = readMetaSource(
|
||||
sourceFile,
|
||||
metaFile,
|
||||
);
|
||||
return {
|
||||
...meta,
|
||||
components: getLabels(componentsMap),
|
||||
utils: utilsMap ? getLabels(utilsMap) : [],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads every `*.meta.ts` file under the package and resolves its
|
||||
* `components`/`utils` identifiers to their declarations.
|
||||
*/
|
||||
function parseMetaFiles(): ParsedMeta[] {
|
||||
const metaFiles = fg
|
||||
.sync(META_GLOB, { cwd: packageRoot, absolute: true })
|
||||
.sort();
|
||||
const metaFiles = findMetaFiles();
|
||||
|
||||
if (metaFiles.length === 0) {
|
||||
return [];
|
||||
@@ -177,30 +301,19 @@ function parseMetaFiles(): ParsedMeta[] {
|
||||
const entries: ResolvedEntry[] = [];
|
||||
|
||||
for (const entry of mapExpression.properties) {
|
||||
let label: string | undefined;
|
||||
let symbol: ts.Symbol | undefined;
|
||||
|
||||
if (ts.isShorthandPropertyAssignment(entry)) {
|
||||
label = entry.name.text;
|
||||
symbol = resolveAliasedSymbol(
|
||||
typeChecker.getShorthandAssignmentValueSymbol(entry),
|
||||
);
|
||||
} else if (
|
||||
ts.isPropertyAssignment(entry) &&
|
||||
ts.isIdentifier(entry.initializer)
|
||||
) {
|
||||
label = ts.isStringLiteral(entry.name)
|
||||
? entry.name.text
|
||||
: entry.name.getText();
|
||||
symbol = resolveAliasedSymbol(
|
||||
typeChecker.getSymbolAtLocation(entry.initializer),
|
||||
);
|
||||
}
|
||||
|
||||
const label = getEntryLabel(entry);
|
||||
if (!label) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let symbol: ts.Symbol | undefined;
|
||||
if (ts.isShorthandPropertyAssignment(entry)) {
|
||||
symbol = typeChecker.getShorthandAssignmentValueSymbol(entry);
|
||||
} else if (ts.isPropertyAssignment(entry)) {
|
||||
symbol = typeChecker.getSymbolAtLocation(entry.initializer);
|
||||
}
|
||||
symbol = resolveAliasedSymbol(symbol);
|
||||
|
||||
const declaration = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
||||
if (!symbol || !declaration) {
|
||||
throw new Error(
|
||||
@@ -222,74 +335,24 @@ function parseMetaFiles(): ParsedMeta[] {
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a `components`/`utils` map from the metadata object and resolves its
|
||||
* entries. Throws when a required map is missing or either map is not an
|
||||
* object literal.
|
||||
*/
|
||||
function resolveEntryMap(
|
||||
metadataNode: ts.ObjectLiteralExpression,
|
||||
key: "components" | "utils",
|
||||
metaFile: string,
|
||||
relMeta: string,
|
||||
required: boolean,
|
||||
): ResolvedEntry[] {
|
||||
const prop = getObjectProperty(metadataNode, key);
|
||||
if (!prop) {
|
||||
if (required) {
|
||||
throw new Error(
|
||||
`[${relMeta}] metadata.${key} must be an object literal.`,
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
if (
|
||||
!ts.isPropertyAssignment(prop) ||
|
||||
!ts.isObjectLiteralExpression(prop.initializer)
|
||||
) {
|
||||
throw new Error(
|
||||
`[${relMeta}] metadata.${key} must be an object literal.`,
|
||||
);
|
||||
}
|
||||
return resolveEntries(prop.initializer, metaFile);
|
||||
}
|
||||
|
||||
function parseMetaFile(metaFile: string): ParsedMeta {
|
||||
const relMeta = path.relative(packageRoot, metaFile);
|
||||
return metaFiles.map((metaFile) => {
|
||||
const sourceFile = tsProgram.getSourceFile(metaFile);
|
||||
if (!sourceFile) {
|
||||
throw new Error(`Could not load meta file: ${relMeta}`);
|
||||
throw new Error(
|
||||
`Could not load meta file: ${path.relative(packageRoot, metaFile)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const metadataNode = findMetadataObject(sourceFile);
|
||||
if (!metadataNode) {
|
||||
throw new Error(`[${relMeta}] Could not find a "metadata" object.`);
|
||||
}
|
||||
|
||||
const name = readStringLiteralProperty(metadataNode, "name");
|
||||
if (!name) {
|
||||
throw new Error(`[${relMeta}] metadata.name must be a string literal.`);
|
||||
}
|
||||
|
||||
const { componentsMap, utilsMap, ...meta } = readMetaSource(
|
||||
sourceFile,
|
||||
metaFile,
|
||||
);
|
||||
return {
|
||||
name,
|
||||
dir: path.relative(packageRoot, path.dirname(metaFile)),
|
||||
metaFile: relMeta,
|
||||
keywords: readStringArrayProperty(metadataNode, "keywords"),
|
||||
related: readStringArrayProperty(metadataNode, "related"),
|
||||
components: resolveEntryMap(
|
||||
metadataNode,
|
||||
"components",
|
||||
metaFile,
|
||||
relMeta,
|
||||
true,
|
||||
),
|
||||
utils: resolveEntryMap(metadataNode, "utils", metaFile, relMeta, false),
|
||||
...meta,
|
||||
components: resolveEntries(componentsMap, metaFile),
|
||||
utils: utilsMap ? resolveEntries(utilsMap, metaFile) : [],
|
||||
};
|
||||
}
|
||||
|
||||
return metaFiles.map(parseMetaFile);
|
||||
});
|
||||
}
|
||||
|
||||
export { parseMetaFiles };
|
||||
export type { ResolvedEntry, ParsedMeta };
|
||||
export { parseMetaFiles, readMetaFiles };
|
||||
export type { ResolvedEntry, ParsedMeta, MetaSummary };
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParsedMeta } from "./parse-meta";
|
||||
import type { MetaSummary, ParsedMeta } from "./parse-meta";
|
||||
|
||||
/** Characters allowed in a Sanity document `_id`. */
|
||||
const SANITY_ID_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
||||
@@ -9,7 +9,7 @@ const SANITY_ID_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
||||
* valid). Kept pure so it can be exercised both by the extractor (which throws
|
||||
* on any error) and by the test-suite.
|
||||
*/
|
||||
function validateMetas(metas: ParsedMeta[]): string[] {
|
||||
function validateMetas(metas: (ParsedMeta | MetaSummary)[]): string[] {
|
||||
const errors: string[] = [];
|
||||
const nameToMeta = new Map<string, string>();
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ const ActionMenuRoot = ({
|
||||
rootElement={rootElement}
|
||||
size={size}
|
||||
>
|
||||
<Menu open={open} onOpenChange={setOpen} modal>
|
||||
<Menu open={open} onOpenChange={setOpen} modal={false}>
|
||||
{children}
|
||||
</Menu>
|
||||
</ActionMenuProvider>
|
||||
|
||||
@@ -120,10 +120,9 @@ function normalizeCount(value: number, fallback: number) {
|
||||
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace, import/export
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace Badge {
|
||||
export type Props = BadgeProps;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/export
|
||||
export { Badge };
|
||||
@@ -221,12 +221,11 @@ function isFieldVisible(
|
||||
return fields?.[key] !== false;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace, import/export
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace DataGridPreferencesRoot {
|
||||
export type Props = DataGridPreferencesProps;
|
||||
}
|
||||
|
||||
export type { DataGridPreferencesProps };
|
||||
// eslint-disable-next-line import/export
|
||||
export { DataGridPreferencesRoot };
|
||||
export default DataGridPreferencesRoot;
|
||||
@@ -140,7 +140,7 @@ const DataGrid = DataGridInternal as unknown as DataGridComponent;
|
||||
DataGrid.Table = DataGridTable;
|
||||
DataGrid.Preferences = DataGridPreferences;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace, import/export
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace DataGrid {
|
||||
export type Props<T = unknown> = DataGridProps<T>;
|
||||
export type Columns<T = unknown> = ColumnDefinitions<T>;
|
||||
@@ -166,6 +166,5 @@ export namespace DataGrid {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/export
|
||||
export { DataGrid, DataGridInternal }; // DataGridRoot needs to be exported b.c. of docgen
|
||||
export default DataGrid;
|
||||
@@ -553,7 +553,7 @@ const DataGridTable = DataGridTableInternal as <RowT>(
|
||||
props: DataGridTableProps<RowT> & React.RefAttributes<HTMLTableElement>,
|
||||
) => React.ReactElement | null;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace, import/export
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace DataGridTable {
|
||||
export type Props<T = unknown> = DataGridTableProps<T>;
|
||||
export type Sorting = TableSortOptions;
|
||||
@@ -566,7 +566,6 @@ export namespace DataGridTable {
|
||||
}
|
||||
|
||||
// docgen doesn't work well with type params, so we let it use DataGridTableInternal instead
|
||||
// eslint-disable-next-line import/export
|
||||
export { DataGridTable, DataGridTableInternal };
|
||||
export type { DataGridTableProps };
|
||||
export default DataGridTable;
|
||||
@@ -64,7 +64,7 @@ const DatePickerRoot = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
onClose,
|
||||
onOpenToggle,
|
||||
strategy,
|
||||
mode,
|
||||
mode: _mode,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
@@ -83,6 +83,8 @@ const DatePickerRoot = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
value: _open,
|
||||
});
|
||||
|
||||
const mode = _mode ?? "single";
|
||||
|
||||
const datePickerOpener = useRef<"from" | "to" | null>(null);
|
||||
|
||||
/* We use state here to insure that anchor is defined if open is true on initial render */
|
||||
@@ -142,7 +144,7 @@ const DatePickerRoot = forwardRef<HTMLDivElement, DatePickerProps>(
|
||||
}}
|
||||
locale={locale}
|
||||
translate={translate}
|
||||
variant={mode ?? "single"}
|
||||
variant={mode}
|
||||
popoverProps={{
|
||||
id: ariaId,
|
||||
strategy,
|
||||
|
||||
@@ -137,7 +137,12 @@ export const Readonly = () => <BasicCombobox readOnly />;
|
||||
|
||||
export const Disabled = () => <BasicCombobox disabled />;
|
||||
|
||||
export const ErrorStory = () => <BasicCombobox error="Du må velge et land." />;
|
||||
export const ErrorStory = () => (
|
||||
<VStack gap="space-16">
|
||||
<BasicCombobox error="Du må velge et land." defaultSelectedOptions={[]} />
|
||||
<BasicCombobox error="Ugyldig land." />
|
||||
</VStack>
|
||||
);
|
||||
ErrorStory.storyName = "Error";
|
||||
|
||||
export const DefaultOpen = () => <BasicCombobox defaultOpen />;
|
||||
@@ -462,33 +467,50 @@ export const Testing = () => {
|
||||
);
|
||||
};
|
||||
|
||||
function WithValidation() {
|
||||
const [error, setError] = useState("Du må velge et land");
|
||||
return (
|
||||
<BasicCombobox
|
||||
options={nordicCountries}
|
||||
defaultSelectedOptions={[]}
|
||||
error={error}
|
||||
onToggleOption={(option) =>
|
||||
setError(option.label === "Island" ? "" : "Du må velge Island")
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const ScreenreaderTesting = () => (
|
||||
<div>
|
||||
<h2>Med grupper</h2>
|
||||
<BasicCombobox options={groupedCountries} defaultSelectedOptions={[]} />
|
||||
|
||||
<h2>Uten grupper</h2>
|
||||
<BasicCombobox options={nordicCountries} defaultSelectedOptions={[]} />
|
||||
|
||||
<h2>Label skjult visuelt</h2>
|
||||
<BasicCombobox options={nordicCountries} hideLabel />
|
||||
|
||||
<h2>Med feilmelding</h2>
|
||||
<WithValidation />
|
||||
|
||||
<h2>Readonly med verdi</h2>
|
||||
<BasicCombobox readOnly />
|
||||
|
||||
<h2>Readonly uten verdi</h2>
|
||||
<BasicCombobox readOnly defaultSelectedOptions={[]} />
|
||||
|
||||
<h2>Disabled</h2>
|
||||
<BasicCombobox disabled />
|
||||
</div>
|
||||
);
|
||||
|
||||
/* TODO:
|
||||
- Mobil: Vurder å ikke fokusere søkefelt automatisk.
|
||||
- Mobil: Vurder å ikke ha virtuelt fokus i det hele tatt hvis man ikke har tastatur
|
||||
Kan kanskje bruke media query, ev. ikke gi noe fokus før man begynner å trykke (som dagens CB. Sjekk hvordan SR takler at ingenting har virtuelt fokus.)
|
||||
Sjekk om det er mulig å flytte virtuelt fokus uten tastatur (typ med skjermleser).
|
||||
- Vurder om fokus skal låses til søkefelt (mest aktuelt ved multiselect).
|
||||
- Åpne på pil ned (og ev. opp)?
|
||||
- Vurder funksjoner fra gamle CB (ikke brukt: dropp, brukt lite: muliggjør med komposisjon, brukt mye: bygg inn støtte)
|
||||
- allowNewValues (er dette ofte egentlig Autocomplete?)
|
||||
- isLoading
|
||||
- maxSelected
|
||||
- Skal den hete noe annet enn Combobox?
|
||||
|
||||
- Navngivning: Vurder å dele opp i separate komponenter for single- og multiselect.
|
||||
- Hvordan unngå CSS-konflikt med dagens CB?
|
||||
- Vurder om Label og Description (og error?) skal være sub-komponenter eller ikke.
|
||||
- Følge Combobox-pattern (mer)? Kan ikke følge det slavisk uansett.
|
||||
Pil opp og ned velger
|
||||
Ikke loop
|
||||
- A11y-sjekk (skjermleser, zoom, høykontrast...)
|
||||
- Beslutningsloggen?
|
||||
- Test med skjermleser
|
||||
|
||||
|
||||
Utfordringer med komposisjon:
|
||||
- Vanskelig å bruke
|
||||
- Utfordrende for oss å endre (ref FormSummary)
|
||||
- Context er litt magisk/uoversiktlig
|
||||
- Description: Hvordan skal root/trigger vite at aria-describedby skal settes?
|
||||
Forslag: Tilby enkeltkomponent for de vanligste tilfellene, men også subkomponentene for fleksibilitet.
|
||||
Kan ev. ha slot/render-props for enkelte ting.
|
||||
Kan ev. bruke children for å kunne bytte ut/skreddersy innholdet i popup.
|
||||
|
||||
*/
|
||||
*/
|
||||
@@ -103,10 +103,12 @@ function ComboboxModal({
|
||||
useEffect(() => {
|
||||
// See "Overlay" section in CSS file for context. This handles the case when
|
||||
// the list doesn't overflow and overscroll-behavior doesn't work on iOS.
|
||||
const scrollToTop = () => (document.documentElement.scrollTop = 0);
|
||||
visualViewport?.addEventListener("scroll", scrollToTop);
|
||||
return () => visualViewport?.removeEventListener("scroll", scrollToTop);
|
||||
}, []);
|
||||
if (open) {
|
||||
const scrollToTop = () => (document.documentElement.scrollTop = 0);
|
||||
visualViewport?.addEventListener("scroll", scrollToTop);
|
||||
return () => visualViewport?.removeEventListener("scroll", scrollToTop);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
||||
@@ -21,32 +21,33 @@ export interface ComboboxPopupProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
// TODO: Bør den hete Dropdown eller Menu? (Og Overlay -> Popup?)
|
||||
export const ComboboxPopup = ({ children }: ComboboxPopupProps) => {
|
||||
const [virtuallyFocusedOptionValue, setVirtuallyFocusedOptionValue] =
|
||||
useState("");
|
||||
const [filterString, setFilterString] = useState("");
|
||||
|
||||
const rootContext = useComboboxRootContext();
|
||||
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(
|
||||
function onOpen() {
|
||||
const canHover = window.matchMedia("(hover: hover)").matches;
|
||||
|
||||
// Virtually focus and scroll to first (selected) option
|
||||
const selectedOptionElm = ref.current?.querySelector<HTMLElement>(
|
||||
'[aria-selected="true"]',
|
||||
);
|
||||
if (selectedOptionElm) {
|
||||
setVirtuallyFocusedOptionValue(selectedOptionElm.dataset.id || "");
|
||||
setTimeout(
|
||||
() => selectedOptionElm.scrollIntoView({ block: "nearest" }),
|
||||
0,
|
||||
);
|
||||
if (canHover) {
|
||||
setVirtuallyFocusedOptionValue(selectedOptionElm.dataset.id || "");
|
||||
}
|
||||
const scrollToSelectedOption = () =>
|
||||
selectedOptionElm.scrollIntoView({ block: "nearest" });
|
||||
setTimeout(scrollToSelectedOption, 0); // For when opened in Floating
|
||||
queueMicrotask(scrollToSelectedOption); // For when opened in Dialog
|
||||
} else {
|
||||
const firstOptionElm =
|
||||
ref.current?.querySelector<HTMLElement>('[role="option"]');
|
||||
if (firstOptionElm) {
|
||||
if (firstOptionElm && canHover) {
|
||||
setVirtuallyFocusedOptionValue(firstOptionElm.dataset.id || "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { ComponentMetadata } from "../../utils/types/metadata";
|
||||
import { RadioCard, RadioCardGroup } from "./index";
|
||||
|
||||
const metadata: ComponentMetadata = {
|
||||
name: "RadioCard",
|
||||
components: {
|
||||
RadioCard,
|
||||
RadioCardGroup,
|
||||
},
|
||||
keywords: ["radio", "radioknapp", "form", "input", "choice", "card"],
|
||||
related: ["Radio"],
|
||||
};
|
||||
|
||||
export { metadata };
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import React from "react";
|
||||
import { HGrid } from "../../primitives/grid";
|
||||
import { renderStoriesForChromatic } from "../../utils/renderStoriesForChromatic";
|
||||
import { RadioCard } from "./RadioCard";
|
||||
import { RadioCardGroup } from "./RadioCardGroup";
|
||||
|
||||
const meta: Meta<typeof RadioCardGroup> = {
|
||||
title: "ds-react/RadioCard",
|
||||
component: RadioCardGroup,
|
||||
subcomponents: { RadioCard },
|
||||
parameters: {
|
||||
chromatic: { disable: true },
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
|
||||
export const Default = () => (
|
||||
<RadioCardGroup
|
||||
legend="Velg leveringsmåte"
|
||||
description="Du kan velge ett alternativ."
|
||||
>
|
||||
<RadioCard value="digital" description="Sendes til innboksen din.">
|
||||
Digital levering
|
||||
</RadioCard>
|
||||
<RadioCard value="post" description="Sendes til folkeregistrert adresse.">
|
||||
Post
|
||||
</RadioCard>
|
||||
</RadioCardGroup>
|
||||
);
|
||||
|
||||
export const HorizontalLayout = () => (
|
||||
<RadioCardGroup
|
||||
legend="Velg leveringsmåte"
|
||||
description="Du kan velge ett alternativ."
|
||||
defaultValue="digital"
|
||||
size="small"
|
||||
>
|
||||
<HGrid columns={{ xs: 1, md: 3 }}>
|
||||
<RadioCard value="digital" description="Sendes til innboksen din.">
|
||||
Digital levering
|
||||
</RadioCard>
|
||||
<RadioCard value="post" description="Sendes til folkeregistrert adresse.">
|
||||
Post
|
||||
</RadioCard>
|
||||
<RadioCard value="seng" description="Blir servert på sengen.">
|
||||
Servert på sengen
|
||||
</RadioCard>
|
||||
</HGrid>
|
||||
</RadioCardGroup>
|
||||
);
|
||||
|
||||
export const Small = () => (
|
||||
<RadioCardGroup legend="Velg leveringsmåte" size="small">
|
||||
<RadioCard value="digital" description="Sendes til innboksen din.">
|
||||
Digital levering
|
||||
</RadioCard>
|
||||
<RadioCard value="post" description="Sendes til folkeregistrert adresse.">
|
||||
Post
|
||||
</RadioCard>
|
||||
</RadioCardGroup>
|
||||
);
|
||||
|
||||
export const ErrorProp = () => (
|
||||
<RadioCardGroup
|
||||
legend="Velg leveringsmåte"
|
||||
error="Du må velge en leveringsmåte."
|
||||
defaultValue="digital"
|
||||
>
|
||||
<RadioCard value="digital" description="Sendes til innboksen din.">
|
||||
Digital levering
|
||||
</RadioCard>
|
||||
<RadioCard value="post" description="Sendes til folkeregistrert adresse.">
|
||||
Post
|
||||
</RadioCard>
|
||||
</RadioCardGroup>
|
||||
);
|
||||
|
||||
export const Readonly = () => (
|
||||
<RadioCardGroup legend="Valgt leveringsmåte" defaultValue="digital" readOnly>
|
||||
<RadioCard value="digital" description="Sendes til innboksen din.">
|
||||
Digital levering
|
||||
</RadioCard>
|
||||
<RadioCard value="post" description="Sendes til folkeregistrert adresse.">
|
||||
Post
|
||||
</RadioCard>
|
||||
</RadioCardGroup>
|
||||
);
|
||||
|
||||
export const Disabled = () => (
|
||||
<RadioCardGroup legend="Velg leveringsmåte" defaultValue="digital" disabled>
|
||||
<RadioCard value="digital" description="Sendes til innboksen din.">
|
||||
Digital levering
|
||||
</RadioCard>
|
||||
<RadioCard value="post" description="Sendes til folkeregistrert adresse.">
|
||||
Post
|
||||
</RadioCard>
|
||||
</RadioCardGroup>
|
||||
);
|
||||
|
||||
export const Chromatic = renderStoriesForChromatic({
|
||||
Default,
|
||||
HorizontalLayout,
|
||||
Small,
|
||||
ErrorProp,
|
||||
Readonly,
|
||||
Disabled,
|
||||
});
|
||||
|
||||
export const ChromaticDark = renderStoriesForChromatic({
|
||||
Default,
|
||||
HorizontalLayout,
|
||||
Small,
|
||||
ErrorProp,
|
||||
Readonly,
|
||||
Disabled,
|
||||
});
|
||||
ChromaticDark.globals = { theme: "dark" };
|
||||
@@ -0,0 +1,41 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { cl } from "../../utils/helpers";
|
||||
import { Radio, RadioCardContextProvider } from "../radio/Radio";
|
||||
import type { RadioProps } from "../radio/types";
|
||||
|
||||
type RadioCardProps = RadioProps;
|
||||
|
||||
/**
|
||||
* Radio option styled as a card.
|
||||
* @see 🏷️ {@link RadioCardProps}
|
||||
* @example
|
||||
* ```tsx
|
||||
* <RadioCardGroup legend="Velg leveringsmåte">
|
||||
* <RadioCard value="digital" description="Sendes til innboksen din.">
|
||||
* Digital levering
|
||||
* </RadioCard>
|
||||
* <RadioCard value="post">Post</RadioCard>
|
||||
* </RadioCardGroup>
|
||||
* ```
|
||||
*/
|
||||
const RadioCard = forwardRef<HTMLInputElement, RadioCardProps>(
|
||||
({ className, ...rest }, ref) => {
|
||||
return (
|
||||
<RadioCardContextProvider>
|
||||
<Radio
|
||||
{...rest}
|
||||
ref={ref}
|
||||
className={cl("aksel-radio-card", className)}
|
||||
/>
|
||||
</RadioCardContextProvider>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace RadioCard {
|
||||
export type Props = RadioCardProps;
|
||||
}
|
||||
|
||||
export { RadioCard };
|
||||
export type { RadioCardProps };
|
||||
@@ -0,0 +1,34 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { cl } from "../../utils/helpers";
|
||||
import RadioGroup, { type RadioGroupProps } from "../radio/RadioGroup";
|
||||
|
||||
type RadioCardGroupProps = RadioGroupProps;
|
||||
|
||||
/**
|
||||
* RadioGroup for `<RadioCard />` elements. Horizontal layout is best suited for a small number of radio cards.
|
||||
* @see 🏷️ {@link RadioCardGroupProps}
|
||||
* @example
|
||||
* ```tsx
|
||||
* <RadioCardGroup legend="Velg leveringsmåte">
|
||||
* <RadioCard value="digital">Digital levering</RadioCard>
|
||||
* <RadioCard value="post">Post</RadioCard>
|
||||
* </RadioCardGroup>
|
||||
* ```
|
||||
*/
|
||||
const RadioCardGroup = forwardRef<HTMLFieldSetElement, RadioCardGroupProps>(
|
||||
({ className, ...rest }, ref) => (
|
||||
<RadioGroup
|
||||
{...rest}
|
||||
ref={ref}
|
||||
className={cl("aksel-radio-card-group", className)}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace RadioCardGroup {
|
||||
export type Props = RadioCardGroupProps;
|
||||
}
|
||||
|
||||
export { RadioCardGroup };
|
||||
export type { RadioCardGroupProps };
|
||||
@@ -0,0 +1,3 @@
|
||||
"use client";
|
||||
export { RadioCard } from "./RadioCard";
|
||||
export { RadioCardGroup } from "./RadioCardGroup";
|
||||
@@ -69,3 +69,22 @@ describe("Uncontrolled RadioGroup", () => {
|
||||
expect(input2.checked).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Radio outside RadioCard", () => {
|
||||
test("does not select when clicking description", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<RadioGroup legend="legend">
|
||||
<Radio value={value1} description="Radio description">
|
||||
{label1}
|
||||
</Radio>
|
||||
</RadioGroup>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByText("Radio description"));
|
||||
|
||||
expect(
|
||||
screen.getByRole<HTMLInputElement>("radio", { name: label1 }).checked,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,19 +1,29 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import React, { forwardRef, useRef } from "react";
|
||||
import { BodyShort } from "../../typography";
|
||||
import { omit, useId } from "../../utils-external";
|
||||
import { cl } from "../../utils/helpers";
|
||||
import { cl, createStrictContext } from "../../utils/helpers";
|
||||
import { useMergeRefs } from "../../utils/hooks";
|
||||
import { RadioInput } from "./radio-input/RadioInput";
|
||||
import type { RadioProps } from "./types";
|
||||
import { useRadio } from "./useRadio";
|
||||
|
||||
const { Provider: RadioCardContextProvider, useContext: useRadioCardContext } =
|
||||
createStrictContext({ name: "radio-card" });
|
||||
|
||||
export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
||||
(props: RadioProps, forwardedRef) => {
|
||||
const { inputProps, size, hasError, readOnly } = useRadio(props);
|
||||
const descriptionId = useId();
|
||||
const cardContext = useRadioCardContext(false);
|
||||
|
||||
const radioRef = useRef<HTMLInputElement>(null);
|
||||
const mergedRefs = useMergeRefs(forwardedRef, radioRef);
|
||||
|
||||
const { className, description, children } = props;
|
||||
|
||||
return (
|
||||
// biome-ignore lint/a11y/noStaticElementInteractions: clickable div for radio card
|
||||
// biome-ignore lint/a11y/useKeyWithClickEvents: clickable div for radio card
|
||||
<div
|
||||
className={cl(className, "aksel-radio", `aksel-radio--${size}`, {
|
||||
"aksel-radio--error": hasError,
|
||||
@@ -21,9 +31,26 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
||||
"aksel-radio--readonly": readOnly,
|
||||
})}
|
||||
data-color={hasError ? "danger" : props["data-color"]}
|
||||
onClick={(event) => {
|
||||
if (inputProps.disabled || readOnly || !cardContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let input and label handle their own click events */
|
||||
const target = event.target;
|
||||
const nativeHandler =
|
||||
target instanceof Element ? target.closest("label, input") : null;
|
||||
|
||||
if (nativeHandler && event.currentTarget.contains(nativeHandler)) {
|
||||
return;
|
||||
}
|
||||
|
||||
radioRef.current?.click();
|
||||
radioRef.current?.focus({ preventScroll: true });
|
||||
}}
|
||||
>
|
||||
<RadioInput
|
||||
ref={forwardedRef}
|
||||
ref={mergedRefs}
|
||||
{...omit(props, [
|
||||
"children",
|
||||
"size",
|
||||
@@ -62,3 +89,4 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
||||
);
|
||||
|
||||
export default Radio;
|
||||
export { RadioCardContextProvider };
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
export { default as Radio } from "./Radio";
|
||||
export { default as RadioGroup, type RadioGroupProps } from "./RadioGroup";
|
||||
export { default as RadioGroup } from "./RadioGroup";
|
||||
export type { RadioGroupProps } from "./RadioGroup";
|
||||
export type { RadioProps } from "./types";
|
||||
@@ -202,6 +202,7 @@ export {
|
||||
type DialogBodyProps,
|
||||
type DialogFooterProps,
|
||||
} from "./dialog";
|
||||
export { Badge } from "./badge";
|
||||
|
||||
/**
|
||||
* Theming
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
export { DataGrid } from "./data-grid";
|
||||
export { Lookup } from "./lookup";
|
||||
export { Badge } from "./badge";
|
||||
export { RadioCard, RadioCardGroup } from "./form/radio-card";
|
||||
@@ -104,7 +104,7 @@ export const ArrowDown: StoryObj = {
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("last");
|
||||
|
||||
pressKey(canvasElement, "ArrowDown");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("first");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("last"); // No looping
|
||||
},
|
||||
};
|
||||
|
||||
@@ -127,7 +127,7 @@ export const ArrowUp: StoryObj = {
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("first");
|
||||
|
||||
pressKey(canvasElement, "ArrowUp");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("last");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("first"); // No looping
|
||||
},
|
||||
};
|
||||
|
||||
@@ -163,7 +163,7 @@ export const PageUpAndPageDownKeys: StoryObj = {
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("14"); // Last
|
||||
|
||||
pressKey(canvasElement, "PageDown");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("14"); // No looping for PageUp/Down
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("14"); // No looping
|
||||
|
||||
pressKey(canvasElement, "PageUp");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("9");
|
||||
@@ -175,7 +175,7 @@ export const PageUpAndPageDownKeys: StoryObj = {
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("0");
|
||||
|
||||
pressKey(canvasElement, "PageUp");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("0"); // No looping for PageUp/Down
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("0"); // No looping
|
||||
},
|
||||
};
|
||||
|
||||
@@ -211,3 +211,54 @@ export const EnterKey: StoryObj<{
|
||||
onClick: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
const ListboxStoryWithSelectedOption = () => {
|
||||
const [virtuallyFocusedOptionId, setVirtuallyFocusedOptionId] = useState("");
|
||||
return (
|
||||
<Listbox setVirtuallyFocusedOptionId={setVirtuallyFocusedOptionId}>
|
||||
<Listbox.Options>
|
||||
<Listbox.Option
|
||||
id="first-item"
|
||||
onClick={() => {}}
|
||||
hasVirtualFocus={virtuallyFocusedOptionId === "first-item"}
|
||||
aria-selected={false}
|
||||
>
|
||||
First item
|
||||
</Listbox.Option>
|
||||
<Listbox.Option
|
||||
id="selected-item"
|
||||
onClick={() => {}}
|
||||
hasVirtualFocus={virtuallyFocusedOptionId === "selected-item"}
|
||||
aria-selected
|
||||
>
|
||||
Selected item
|
||||
</Listbox.Option>
|
||||
<Listbox.Option
|
||||
id="last-item"
|
||||
onClick={() => {}}
|
||||
hasVirtualFocus={virtuallyFocusedOptionId === "last-item"}
|
||||
aria-selected={false}
|
||||
>
|
||||
Last item
|
||||
</Listbox.Option>
|
||||
</Listbox.Options>
|
||||
</Listbox>
|
||||
);
|
||||
};
|
||||
|
||||
// When there's a selected option, but no option has virtual focus,
|
||||
// pressing ArrowUp or ArrowDown should focus the selected option.
|
||||
export const ArrowDownFocusesSelectedOption: StoryObj = {
|
||||
render: ListboxStoryWithSelectedOption,
|
||||
play: async ({ canvasElement }) => {
|
||||
pressKey(canvasElement, "ArrowDown");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("selected-item");
|
||||
},
|
||||
};
|
||||
export const ArrowUpFocusesSelectedOption: StoryObj = {
|
||||
render: ListboxStoryWithSelectedOption,
|
||||
play: async ({ canvasElement }) => {
|
||||
pressKey(canvasElement, "ArrowUp");
|
||||
expect(getVirtuallyFocusedValue(canvasElement)).toBe("selected-item");
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
/** biome-ignore-all lint/a11y/useKeyWithMouseEvents: We know what we are doing */
|
||||
import React from "react";
|
||||
import { cl } from "../../../helpers";
|
||||
import { useMedia } from "../../../hooks";
|
||||
import { useListboxContext } from "../root/Listbox.context";
|
||||
|
||||
export interface ListboxOptionsProps extends Omit<
|
||||
@@ -12,6 +13,8 @@ export interface ListboxOptionsProps extends Omit<
|
||||
|
||||
function ListboxOptions({ children, ...rest }: ListboxOptionsProps) {
|
||||
const { setVirtuallyFocusedOptionId } = useListboxContext();
|
||||
const cannotHover = useMedia("(hover: none)");
|
||||
|
||||
return (
|
||||
<div
|
||||
{...rest}
|
||||
@@ -19,6 +22,11 @@ function ListboxOptions({ children, ...rest }: ListboxOptionsProps) {
|
||||
role="listbox"
|
||||
tabIndex={-1}
|
||||
onMouseOver={(event) => {
|
||||
if (cannotHover) {
|
||||
// onMouseOver can be triggered by touch, but we don't want virtual focus in that case.
|
||||
// We assume you are using touch if the primary input mechanism cannot hover.
|
||||
return;
|
||||
}
|
||||
const target = event.target as HTMLElement;
|
||||
const optionEl: HTMLElement | null = target.closest('[role="option"]');
|
||||
if (optionEl) {
|
||||
|
||||
@@ -79,18 +79,16 @@ const ListboxComponent = forwardRef<HTMLDivElement, ListboxProps>(
|
||||
) => {
|
||||
event.preventDefault();
|
||||
if (!focusedOptionElm) {
|
||||
virtuallyFocusOption(getFallback());
|
||||
const fallback =
|
||||
listbox.querySelector<HTMLElement>('[aria-selected="true"]') ||
|
||||
getFallback();
|
||||
virtuallyFocusOption(fallback);
|
||||
return;
|
||||
}
|
||||
const nextOption = getNextElement(focusedOptionElm);
|
||||
if (!nextOption) {
|
||||
// TODO: Consider whether we want looping.
|
||||
// If yes, consider if PageUp/PageDown should loop too.
|
||||
// If no, remember to update tests.
|
||||
virtuallyFocusOption(getFallback());
|
||||
return;
|
||||
if (nextOption) {
|
||||
virtuallyFocusOption(nextOption);
|
||||
}
|
||||
virtuallyFocusOption(nextOption);
|
||||
};
|
||||
|
||||
switch (event.key) {
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @navikt/ds-tailwind
|
||||
|
||||
## 8.19.1
|
||||
|
||||
## 8.19.0
|
||||
|
||||
## 8.18.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- v3 preset adds `rounded-ax-*` and no longer lists radius tokens as colors ([#5329](https://github.com/navikt/aksel/pull/5329))
|
||||
|
||||
- v4 theme now uses `@theme inline`, so dark mode and `data-color` work with Tailwind utilities. Font sizes are available as `text-ax-*` and font family as `font-ax-font-family` (`font-family-ax-font-family` is deprecated) ([#5329](https://github.com/navikt/aksel/pull/5329))
|
||||
|
||||
## 8.17.2
|
||||
|
||||
## 8.17.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tailwind",
|
||||
"version": "8.17.2",
|
||||
"version": "8.19.1",
|
||||
"description": "Tailwind config based on @navikt/ds-tokens",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -28,10 +28,10 @@
|
||||
},
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/tailwind/",
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^8.17.2",
|
||||
"@navikt/ds-tokens": "^8.19.1",
|
||||
"@types/node": "^24.10.9",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tsx": "^4.23.1",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ const nonColorTokens = [
|
||||
"font-size",
|
||||
"font-line-height",
|
||||
"font-family",
|
||||
"border-radius",
|
||||
"radius",
|
||||
"breakpoint",
|
||||
"opacity",
|
||||
];
|
||||
@@ -48,6 +48,7 @@ export const config = {
|
||||
lineHeight: prefixTokens(extractTokensForCategory("font-line-height")),
|
||||
fontFamily: prefixTokens(extractTokensForCategory("font-family")),
|
||||
opacity: prefixTokens(extractTokensForCategory("opacity")),
|
||||
borderRadius: prefixTokens(extractTokensForCategory("radius")),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -48,7 +48,8 @@ export const v4Config = {
|
||||
};
|
||||
|
||||
function generateThemeCSS(): string {
|
||||
const lines: string[] = ["@theme {"];
|
||||
/* `inline` makes utilities reference `--ax-*` directly, so nested themes (`.dark`, `data-color`, `<Theme>`) resolve correctly */
|
||||
const lines: string[] = ["@theme inline {"];
|
||||
|
||||
for (const [key, value] of Object.entries(v4Config.colors)) {
|
||||
lines.push(` --color-ax-${key}: ${value};`);
|
||||
@@ -61,7 +62,7 @@ function generateThemeCSS(): string {
|
||||
|
||||
lines.push("");
|
||||
for (const [key, value] of Object.entries(v4Config.fontSizes)) {
|
||||
lines.push(` --font-size-ax-${key}: ${value};`);
|
||||
lines.push(` --text-ax-${key}: ${value};`);
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
@@ -76,6 +77,8 @@ function generateThemeCSS(): string {
|
||||
|
||||
lines.push("");
|
||||
for (const [key, value] of Object.entries(v4Config.fontFamilies)) {
|
||||
lines.push(` --font-ax-${key}: ${value};`);
|
||||
/* @deprecated Kept for backwards compatibility, use `font-ax-*` */
|
||||
lines.push(` --font-family-ax-${key}: ${value};`);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ describe("Tailwind v3 config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
/* https://tailwindcss.com/docs/theme#theme-variable-namespaces */
|
||||
const allowedVariables = [
|
||||
"--color-",
|
||||
"--font-",
|
||||
@@ -40,18 +41,32 @@ const allowedVariables = [
|
||||
"--opacity-",
|
||||
];
|
||||
|
||||
const getVariables = (css: string) =>
|
||||
css
|
||||
.split("\n")
|
||||
.filter((line) => line.trim().startsWith("--"))
|
||||
.map((line) => line.trim().split(":")[0]);
|
||||
|
||||
const nonColorCategories = [
|
||||
"space",
|
||||
"shadow",
|
||||
"font-weight",
|
||||
"font-size",
|
||||
"font-line-height",
|
||||
"font-family",
|
||||
"radius",
|
||||
"breakpoint",
|
||||
"opacity",
|
||||
];
|
||||
|
||||
describe("Tailwind v4 config", () => {
|
||||
test("should have correct color tokens without non-color categories", () => {
|
||||
const colorKeys = Object.keys(v4Config.colors);
|
||||
expect(colorKeys).not.toContain("spacing");
|
||||
expect(colorKeys).not.toContain("shadow");
|
||||
expect(colorKeys).not.toContain("font-weight");
|
||||
expect(colorKeys).not.toContain("font-size");
|
||||
expect(colorKeys).not.toContain("font-line-height");
|
||||
expect(colorKeys).not.toContain("font-family");
|
||||
expect(colorKeys).not.toContain("border-radius");
|
||||
expect(colorKeys).not.toContain("breakpoint");
|
||||
expect(colorKeys.some((k) => k.includes("radius"))).toBe(false);
|
||||
const invalid = colorKeys.filter((key) =>
|
||||
nonColorCategories.some((category) => key.includes(category)),
|
||||
);
|
||||
|
||||
expect(invalid).toEqual([]);
|
||||
});
|
||||
|
||||
test("should have shadow tokens", () => {
|
||||
@@ -76,19 +91,32 @@ describe("Tailwind v4 config", () => {
|
||||
expect(Object.keys(v4Config.radius).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("uses `@theme inline` so aliases resolve where the utility is used", () => {
|
||||
expect(generateThemeCSS().split("\n")[0]).toBe("@theme inline {");
|
||||
});
|
||||
|
||||
test("all generated variables should use allowed Tailwind v4 theme namespaces", () => {
|
||||
const css = generateThemeCSS();
|
||||
const variableLines = css
|
||||
.split("\n")
|
||||
.filter((line) => line.trim().startsWith("--"))
|
||||
.map((line) => line.trim().split(":")[0]);
|
||||
const variables = getVariables(generateThemeCSS());
|
||||
|
||||
expect(variableLines.length).toBeGreaterThan(0);
|
||||
expect(variables.length).toBeGreaterThan(0);
|
||||
|
||||
const invalid = variableLines.filter(
|
||||
const invalid = variables.filter(
|
||||
(v) => !allowedVariables.some((prefix) => v.startsWith(prefix)),
|
||||
);
|
||||
|
||||
expect(invalid).toEqual([]);
|
||||
});
|
||||
|
||||
test("font families use the `--font-*` namespace", () => {
|
||||
expect(getVariables(generateThemeCSS())).toContain("--font-ax-font-family");
|
||||
});
|
||||
|
||||
test("font-size keys don't collide with color keys in `text-ax-*`", () => {
|
||||
const colorKeys = Object.keys(v4Config.colors);
|
||||
const collisions = Object.keys(v4Config.fontSizes).filter((key) =>
|
||||
colorKeys.includes(key),
|
||||
);
|
||||
|
||||
expect(collisions).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,11 @@
|
||||
# @navikt/ds-tokens
|
||||
|
||||
## 8.19.1
|
||||
|
||||
## 8.19.0
|
||||
|
||||
## 8.18.0
|
||||
|
||||
## 8.17.2
|
||||
|
||||
## 8.17.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tokens",
|
||||
"version": "8.17.2",
|
||||
"version": "8.19.1",
|
||||
"description": "Design-tokens for Nav designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -35,12 +35,13 @@
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/styling/design-tokens/",
|
||||
"devDependencies": {
|
||||
"@figma/plugin-typings": "^1.131.0",
|
||||
"@types/lodash": "4.17.25",
|
||||
"colorjs.io": "^0.7.1",
|
||||
"esbuild": "^0.28.2",
|
||||
"lightningcss": "^1.33.0",
|
||||
"lodash": "4.18.1",
|
||||
"style-dictionary": "^4.1.1",
|
||||
"tsx": "^4.23.1",
|
||||
"style-dictionary": "^5.5.5",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
|
||||
@@ -45,15 +45,16 @@ export const getTokensForCollection = async (
|
||||
return dictionary.allTokens
|
||||
.filter((token) => !token.figmaIgnore)
|
||||
.map((token) => {
|
||||
const reference = getReferences(token.original, dictionary.tokens);
|
||||
const references = getReferences(token.original, dictionary.tokens);
|
||||
/*
|
||||
* Currently only supports 1 level of references.
|
||||
* The array always includes the token itself as the last element.
|
||||
*/
|
||||
return reference.length > 0
|
||||
return references.length > 1
|
||||
? prepareToken(
|
||||
{
|
||||
...(token as TransformedTokenWithScopes),
|
||||
alias: createTokenName(reference[0]),
|
||||
alias: createTokenName(references[0]),
|
||||
},
|
||||
dictionary,
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ export type StyleDictionaryToken<T extends TokenTypes> = {
|
||||
* Token value
|
||||
* @example "#000000"
|
||||
* @example "1px"
|
||||
* @example "{a.neutral.100.value}"
|
||||
* @example "{a.neutral.100}"
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const accentSemanticTokenConfig = {
|
||||
bg: {
|
||||
"accent-soft": {
|
||||
value: "{ax.accent.100.value}",
|
||||
value: "{ax.accent.100}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"accent-softA": {
|
||||
value: "{ax.accent.100A.value}",
|
||||
value: "{ax.accent.100A}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"accent-moderate": {
|
||||
value: "{ax.accent.200.value}",
|
||||
value: "{ax.accent.200}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border.",
|
||||
},
|
||||
"accent-moderateA": {
|
||||
value: "{ax.accent.200A.value}",
|
||||
value: "{ax.accent.200A}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"accent-moderate-hover": {
|
||||
value: "{ax.accent.300.value}",
|
||||
value: "{ax.accent.300}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"accent-moderate-hoverA": {
|
||||
value: "{ax.accent.300A.value}",
|
||||
value: "{ax.accent.300A}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer (f.eks. button accent secondary hover). Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"accent-moderate-pressed": {
|
||||
value: "{ax.accent.400.value}",
|
||||
value: "{ax.accent.400}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"accent-moderate-pressedA": {
|
||||
value: "{ax.accent.400A.value}",
|
||||
value: "{ax.accent.400A}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer (f.eks. button accent secondary active). Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"accent-strong": {
|
||||
value: "{ax.accent.600.value}",
|
||||
value: "{ax.accent.600}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer (f.eks. button accent primary).",
|
||||
},
|
||||
"accent-strong-hover": {
|
||||
value: "{ax.accent.700.value}",
|
||||
value: "{ax.accent.700}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer (f.eks. button accent primary hover).",
|
||||
},
|
||||
"accent-strong-pressed": {
|
||||
value: "{ax.accent.800.value}",
|
||||
value: "{ax.accent.800}",
|
||||
type: "color",
|
||||
group: "background.accent",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const accentSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
accent: {
|
||||
value: "{ax.accent.1000.value}",
|
||||
value: "{ax.accent.1000}",
|
||||
type: "color",
|
||||
group: "text.accent",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen accent. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"accent-subtle": {
|
||||
value: "{ax.accent.800.value}",
|
||||
value: "{ax.accent.800}",
|
||||
type: "color",
|
||||
group: "text.accent",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen accent. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"accent-decoration": {
|
||||
value: "{ax.accent.600.value}",
|
||||
value: "{ax.accent.600}",
|
||||
type: "color",
|
||||
group: "text.accent",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"accent-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.accent",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const accentSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
accent: {
|
||||
value: "{ax.accent.600.value}",
|
||||
value: "{ax.accent.600}",
|
||||
type: "color",
|
||||
group: "border.accent",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"accent-subtle": {
|
||||
value: "{ax.accent.400.value}",
|
||||
value: "{ax.accent.400}",
|
||||
type: "color",
|
||||
group: "border.accent",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"accent-subtleA": {
|
||||
value: "{ax.accent.400A.value}",
|
||||
value: "{ax.accent.400A}",
|
||||
type: "color",
|
||||
group: "border.accent",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"accent-strong": {
|
||||
value: "{ax.accent.700.value}",
|
||||
value: "{ax.accent.700}",
|
||||
type: "color",
|
||||
group: "border.accent",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const brandBeigeSemanticTokenConfig = {
|
||||
bg: {
|
||||
"brand-beige-soft": {
|
||||
value: "{ax.brand-beige.100.value}",
|
||||
value: "{ax.brand-beige.100}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"brand-beige-softA": {
|
||||
value: "{ax.brand-beige.100A.value}",
|
||||
value: "{ax.brand-beige.100A}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-beige-moderate": {
|
||||
value: "{ax.brand-beige.200.value}",
|
||||
value: "{ax.brand-beige.200}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-beige-moderateA": {
|
||||
value: "{ax.brand-beige.200A.value}",
|
||||
value: "{ax.brand-beige.200A}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-beige-moderate-hover": {
|
||||
value: "{ax.brand-beige.300.value}",
|
||||
value: "{ax.brand-beige.300}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-beige-moderate-hoverA": {
|
||||
value: "{ax.brand-beige.300A.value}",
|
||||
value: "{ax.brand-beige.300A}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-beige-moderate-pressed": {
|
||||
value: "{ax.brand-beige.400.value}",
|
||||
value: "{ax.brand-beige.400}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-beige-moderate-pressedA": {
|
||||
value: "{ax.brand-beige.400A.value}",
|
||||
value: "{ax.brand-beige.400A}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-beige-strong": {
|
||||
value: "{ax.brand-beige.600.value}",
|
||||
value: "{ax.brand-beige.600}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"brand-beige-strong-hover": {
|
||||
value: "{ax.brand-beige.700.value}",
|
||||
value: "{ax.brand-beige.700}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"brand-beige-strong-pressed": {
|
||||
value: "{ax.brand-beige.800.value}",
|
||||
value: "{ax.brand-beige.800}",
|
||||
type: "color",
|
||||
group: "background.brand-beige",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const brandBeigeSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
"brand-beige": {
|
||||
value: "{ax.brand-beige.1000.value}",
|
||||
value: "{ax.brand-beige.1000}",
|
||||
type: "color",
|
||||
group: "text.brand-beige",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen brand beige. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"brand-beige-subtle": {
|
||||
value: "{ax.brand-beige.800.value}",
|
||||
value: "{ax.brand-beige.800}",
|
||||
type: "color",
|
||||
group: "text.brand-beige",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen brand beige. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"brand-beige-decoration": {
|
||||
value: "{ax.brand-beige.600.value}",
|
||||
value: "{ax.brand-beige.600}",
|
||||
type: "color",
|
||||
group: "text.brand-beige",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"brand-beige-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.brand-beige",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const brandBeigeSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
"brand-beige": {
|
||||
value: "{ax.brand-beige.600.value}",
|
||||
value: "{ax.brand-beige.600}",
|
||||
type: "color",
|
||||
group: "border.brand-beige",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"brand-beige-subtle": {
|
||||
value: "{ax.brand-beige.400.value}",
|
||||
value: "{ax.brand-beige.400}",
|
||||
type: "color",
|
||||
group: "border.brand-beige",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"brand-beige-subtleA": {
|
||||
value: "{ax.brand-beige.400A.value}",
|
||||
value: "{ax.brand-beige.400A}",
|
||||
type: "color",
|
||||
group: "border.brand-beige",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-beige-strong": {
|
||||
value: "{ax.brand-beige.700.value}",
|
||||
value: "{ax.brand-beige.700}",
|
||||
type: "color",
|
||||
group: "border.brand-beige",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const brandBlueSemanticTokenConfig = {
|
||||
bg: {
|
||||
"brand-blue-soft": {
|
||||
value: "{ax.brand-blue.100.value}",
|
||||
value: "{ax.brand-blue.100}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"brand-blue-softA": {
|
||||
value: "{ax.brand-blue.100A.value}",
|
||||
value: "{ax.brand-blue.100A}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-blue-moderate": {
|
||||
value: "{ax.brand-blue.200.value}",
|
||||
value: "{ax.brand-blue.200}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-blue-moderateA": {
|
||||
value: "{ax.brand-blue.200A.value}",
|
||||
value: "{ax.brand-blue.200A}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-blue-moderate-hover": {
|
||||
value: "{ax.brand-blue.300.value}",
|
||||
value: "{ax.brand-blue.300}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-blue-moderate-hoverA": {
|
||||
value: "{ax.brand-blue.300A.value}",
|
||||
value: "{ax.brand-blue.300A}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-blue-moderate-pressed": {
|
||||
value: "{ax.brand-blue.400.value}",
|
||||
value: "{ax.brand-blue.400}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-blue-moderate-pressedA": {
|
||||
value: "{ax.brand-blue.400A.value}",
|
||||
value: "{ax.brand-blue.400A}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-blue-strong": {
|
||||
value: "{ax.brand-blue.600.value}",
|
||||
value: "{ax.brand-blue.600}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"brand-blue-strong-hover": {
|
||||
value: "{ax.brand-blue.700.value}",
|
||||
value: "{ax.brand-blue.700}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"brand-blue-strong-pressed": {
|
||||
value: "{ax.brand-blue.800.value}",
|
||||
value: "{ax.brand-blue.800}",
|
||||
type: "color",
|
||||
group: "background.brand-blue",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const brandBlueSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
"brand-blue": {
|
||||
value: "{ax.brand-blue.1000.value}",
|
||||
value: "{ax.brand-blue.1000}",
|
||||
type: "color",
|
||||
group: "text.brand-blue",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen brand blue. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"brand-blue-subtle": {
|
||||
value: "{ax.brand-blue.800.value}",
|
||||
value: "{ax.brand-blue.800}",
|
||||
type: "color",
|
||||
group: "text.brand-blue",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen brand blue. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"brand-blue-decoration": {
|
||||
value: "{ax.brand-blue.600.value}",
|
||||
value: "{ax.brand-blue.600}",
|
||||
type: "color",
|
||||
group: "text.brand-blue",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"brand-blue-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.brand-blue",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const brandBlueSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
"brand-blue": {
|
||||
value: "{ax.brand-blue.600.value}",
|
||||
value: "{ax.brand-blue.600}",
|
||||
type: "color",
|
||||
group: "border.brand-blue",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"brand-blue-subtle": {
|
||||
value: "{ax.brand-blue.400.value}",
|
||||
value: "{ax.brand-blue.400}",
|
||||
type: "color",
|
||||
group: "border.brand-blue",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"brand-blue-subtleA": {
|
||||
value: "{ax.brand-blue.400A.value}",
|
||||
value: "{ax.brand-blue.400A}",
|
||||
type: "color",
|
||||
group: "border.brand-blue",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-blue-strong": {
|
||||
value: "{ax.brand-blue.700.value}",
|
||||
value: "{ax.brand-blue.700}",
|
||||
type: "color",
|
||||
group: "border.brand-blue",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const brandMagentaSemanticTokenConfig = {
|
||||
bg: {
|
||||
"brand-magenta-soft": {
|
||||
value: "{ax.brand-magenta.100.value}",
|
||||
value: "{ax.brand-magenta.100}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"brand-magenta-softA": {
|
||||
value: "{ax.brand-magenta.100A.value}",
|
||||
value: "{ax.brand-magenta.100A}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-magenta-moderate": {
|
||||
value: "{ax.brand-magenta.200.value}",
|
||||
value: "{ax.brand-magenta.200}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-magenta-moderateA": {
|
||||
value: "{ax.brand-magenta.200A.value}",
|
||||
value: "{ax.brand-magenta.200A}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-magenta-moderate-hover": {
|
||||
value: "{ax.brand-magenta.300.value}",
|
||||
value: "{ax.brand-magenta.300}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-magenta-moderate-hoverA": {
|
||||
value: "{ax.brand-magenta.300A.value}",
|
||||
value: "{ax.brand-magenta.300A}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-magenta-moderate-pressed": {
|
||||
value: "{ax.brand-magenta.400.value}",
|
||||
value: "{ax.brand-magenta.400}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"brand-magenta-moderate-pressedA": {
|
||||
value: "{ax.brand-magenta.400A.value}",
|
||||
value: "{ax.brand-magenta.400A}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-magenta-strong": {
|
||||
value: "{ax.brand-magenta.600.value}",
|
||||
value: "{ax.brand-magenta.600}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"brand-magenta-strong-hover": {
|
||||
value: "{ax.brand-magenta.700.value}",
|
||||
value: "{ax.brand-magenta.700}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"brand-magenta-strong-pressed": {
|
||||
value: "{ax.brand-magenta.800.value}",
|
||||
value: "{ax.brand-magenta.800}",
|
||||
type: "color",
|
||||
group: "background.brand-magenta",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const brandMagentaSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
"brand-magenta": {
|
||||
value: "{ax.brand-magenta.1000.value}",
|
||||
value: "{ax.brand-magenta.1000}",
|
||||
type: "color",
|
||||
group: "text.brand-magenta",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen brand magenta. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"brand-magenta-subtle": {
|
||||
value: "{ax.brand-magenta.800.value}",
|
||||
value: "{ax.brand-magenta.800}",
|
||||
type: "color",
|
||||
group: "text.brand-magenta",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen brand magenta. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"brand-magenta-decoration": {
|
||||
value: "{ax.brand-magenta.600.value}",
|
||||
value: "{ax.brand-magenta.600}",
|
||||
type: "color",
|
||||
group: "text.brand-magenta",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"brand-magenta-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.brand-magenta",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const brandMagentaSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
"brand-magenta": {
|
||||
value: "{ax.brand-magenta.600.value}",
|
||||
value: "{ax.brand-magenta.600}",
|
||||
type: "color",
|
||||
group: "border.brand-magenta",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"brand-magenta-subtle": {
|
||||
value: "{ax.brand-magenta.400.value}",
|
||||
value: "{ax.brand-magenta.400}",
|
||||
type: "color",
|
||||
group: "border.brand-magenta",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"brand-magenta-subtleA": {
|
||||
value: "{ax.brand-magenta.400A.value}",
|
||||
value: "{ax.brand-magenta.400A}",
|
||||
type: "color",
|
||||
group: "border.brand-magenta",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"brand-magenta-strong": {
|
||||
value: "{ax.brand-magenta.700.value}",
|
||||
value: "{ax.brand-magenta.700}",
|
||||
type: "color",
|
||||
group: "border.brand-magenta",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const dangerSemanticTokenConfig = {
|
||||
bg: {
|
||||
"danger-soft": {
|
||||
value: "{ax.danger.100.value}",
|
||||
value: "{ax.danger.100}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"danger-softA": {
|
||||
value: "{ax.danger.100A.value}",
|
||||
value: "{ax.danger.100A}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"danger-moderate": {
|
||||
value: "{ax.danger.200.value}",
|
||||
value: "{ax.danger.200}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border (f.eks. alert error).",
|
||||
},
|
||||
"danger-moderateA": {
|
||||
value: "{ax.danger.200A.value}",
|
||||
value: "{ax.danger.200A}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"danger-moderate-hover": {
|
||||
value: "{ax.danger.300.value}",
|
||||
value: "{ax.danger.300}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"danger-moderate-hoverA": {
|
||||
value: "{ax.danger.300A.value}",
|
||||
value: "{ax.danger.300A}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"danger-moderate-pressed": {
|
||||
value: "{ax.danger.400.value}",
|
||||
value: "{ax.danger.400}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"danger-moderate-pressedA": {
|
||||
value: "{ax.danger.400A.value}",
|
||||
value: "{ax.danger.400A}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"danger-strong": {
|
||||
value: "{ax.danger.600.value}",
|
||||
value: "{ax.danger.600}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer (f.eks. button danger primary).",
|
||||
},
|
||||
"danger-strong-hover": {
|
||||
value: "{ax.danger.700.value}",
|
||||
value: "{ax.danger.700}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer (f.eks. button danger primary hover).",
|
||||
},
|
||||
"danger-strong-pressed": {
|
||||
value: "{ax.danger.800.value}",
|
||||
value: "{ax.danger.800}",
|
||||
type: "color",
|
||||
group: "background.danger",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const dangerSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
danger: {
|
||||
value: "{ax.danger.1000.value}",
|
||||
value: "{ax.danger.1000}",
|
||||
type: "color",
|
||||
group: "text.danger",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen danger. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"danger-subtle": {
|
||||
value: "{ax.danger.800.value}",
|
||||
value: "{ax.danger.800}",
|
||||
type: "color",
|
||||
group: "text.danger",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen danger. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"danger-decoration": {
|
||||
value: "{ax.danger.600.value}",
|
||||
value: "{ax.danger.600}",
|
||||
type: "color",
|
||||
group: "text.danger",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"danger-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.danger",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const dangerSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
danger: {
|
||||
value: "{ax.danger.600.value}",
|
||||
value: "{ax.danger.600}",
|
||||
type: "color",
|
||||
group: "border.danger",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"danger-subtle": {
|
||||
value: "{ax.danger.400.value}",
|
||||
value: "{ax.danger.400}",
|
||||
type: "color",
|
||||
group: "border.danger",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"danger-subtleA": {
|
||||
value: "{ax.danger.400A.value}",
|
||||
value: "{ax.danger.400A}",
|
||||
type: "color",
|
||||
group: "border.danger",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"danger-strong": {
|
||||
value: "{ax.danger.700.value}",
|
||||
value: "{ax.danger.700}",
|
||||
type: "color",
|
||||
group: "border.danger",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const infoSemanticTokenConfig = {
|
||||
bg: {
|
||||
"info-soft": {
|
||||
value: "{ax.info.100.value}",
|
||||
value: "{ax.info.100}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"info-softA": {
|
||||
value: "{ax.info.100A.value}",
|
||||
value: "{ax.info.100A}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"info-moderate": {
|
||||
value: "{ax.info.200.value}",
|
||||
value: "{ax.info.200}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border (f.eks. alert info).",
|
||||
},
|
||||
"info-moderateA": {
|
||||
value: "{ax.info.200A.value}",
|
||||
value: "{ax.info.200A}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"info-moderate-hover": {
|
||||
value: "{ax.info.300.value}",
|
||||
value: "{ax.info.300}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"info-moderate-hoverA": {
|
||||
value: "{ax.info.300A.value}",
|
||||
value: "{ax.info.300A}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"info-moderate-pressed": {
|
||||
value: "{ax.info.400.value}",
|
||||
value: "{ax.info.400}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"info-moderate-pressedA": {
|
||||
value: "{ax.info.400A.value}",
|
||||
value: "{ax.info.400A}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"info-strong": {
|
||||
value: "{ax.info.600.value}",
|
||||
value: "{ax.info.600}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"info-strong-hover": {
|
||||
value: "{ax.info.700.value}",
|
||||
value: "{ax.info.700}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"info-strong-pressed": {
|
||||
value: "{ax.info.800.value}",
|
||||
value: "{ax.info.800}",
|
||||
type: "color",
|
||||
group: "background.info",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const infoSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
info: {
|
||||
value: "{ax.info.1000.value}",
|
||||
value: "{ax.info.1000}",
|
||||
type: "color",
|
||||
group: "text.info",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen info. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"info-subtle": {
|
||||
value: "{ax.info.800.value}",
|
||||
value: "{ax.info.800}",
|
||||
type: "color",
|
||||
group: "text.info",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen info. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"info-decoration": {
|
||||
value: "{ax.info.600.value}",
|
||||
value: "{ax.info.600}",
|
||||
type: "color",
|
||||
group: "text.info",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"info-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.info",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const infoSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
info: {
|
||||
value: "{ax.info.600.value}",
|
||||
value: "{ax.info.600}",
|
||||
type: "color",
|
||||
group: "border.info",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"info-subtle": {
|
||||
value: "{ax.info.400.value}",
|
||||
value: "{ax.info.400}",
|
||||
type: "color",
|
||||
group: "border.info",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"info-subtleA": {
|
||||
value: "{ax.info.400A.value}",
|
||||
value: "{ax.info.400A}",
|
||||
type: "color",
|
||||
group: "border.info",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"info-strong": {
|
||||
value: "{ax.info.700.value}",
|
||||
value: "{ax.info.700}",
|
||||
type: "color",
|
||||
group: "border.info",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const metaLimeSemanticTokenConfig = {
|
||||
bg: {
|
||||
"meta-lime-soft": {
|
||||
value: "{ax.meta-lime.100.value}",
|
||||
value: "{ax.meta-lime.100}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"meta-lime-softA": {
|
||||
value: "{ax.meta-lime.100A.value}",
|
||||
value: "{ax.meta-lime.100A}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-lime-moderate": {
|
||||
value: "{ax.meta-lime.200.value}",
|
||||
value: "{ax.meta-lime.200}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border.",
|
||||
},
|
||||
"meta-lime-moderateA": {
|
||||
value: "{ax.meta-lime.200A.value}",
|
||||
value: "{ax.meta-lime.200A}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-lime-moderate-hover": {
|
||||
value: "{ax.meta-lime.300.value}",
|
||||
value: "{ax.meta-lime.300}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"meta-lime-moderate-hoverA": {
|
||||
value: "{ax.meta-lime.300A.value}",
|
||||
value: "{ax.meta-lime.300A}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-lime-moderate-pressed": {
|
||||
value: "{ax.meta-lime.400.value}",
|
||||
value: "{ax.meta-lime.400}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"meta-lime-moderate-pressedA": {
|
||||
value: "{ax.meta-lime.400A.value}",
|
||||
value: "{ax.meta-lime.400A}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-lime-strong": {
|
||||
value: "{ax.meta-lime.600.value}",
|
||||
value: "{ax.meta-lime.600}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"meta-lime-strong-hover": {
|
||||
value: "{ax.meta-lime.700.value}",
|
||||
value: "{ax.meta-lime.700}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"meta-lime-strong-pressed": {
|
||||
value: "{ax.meta-lime.800.value}",
|
||||
value: "{ax.meta-lime.800}",
|
||||
type: "color",
|
||||
group: "background.meta-lime",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const metaLimeSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
"meta-lime": {
|
||||
value: "{ax.meta-lime.1000.value}",
|
||||
value: "{ax.meta-lime.1000}",
|
||||
type: "color",
|
||||
group: "text.meta-lime",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen meta lime. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"meta-lime-subtle": {
|
||||
value: "{ax.meta-lime.800.value}",
|
||||
value: "{ax.meta-lime.800}",
|
||||
type: "color",
|
||||
group: "text.meta-lime",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen meta lime. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"meta-lime-decoration": {
|
||||
value: "{ax.meta-lime.600.value}",
|
||||
value: "{ax.meta-lime.600}",
|
||||
type: "color",
|
||||
group: "text.meta-lime",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"meta-lime-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.meta-lime",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const metaLimeSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
"meta-lime": {
|
||||
value: "{ax.meta-lime.600.value}",
|
||||
value: "{ax.meta-lime.600}",
|
||||
type: "color",
|
||||
group: "border.meta-lime",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"meta-lime-subtle": {
|
||||
value: "{ax.meta-lime.400.value}",
|
||||
value: "{ax.meta-lime.400}",
|
||||
type: "color",
|
||||
group: "border.meta-lime",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"meta-lime-subtleA": {
|
||||
value: "{ax.meta-lime.400A.value}",
|
||||
value: "{ax.meta-lime.400A}",
|
||||
type: "color",
|
||||
group: "border.meta-lime",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-lime-strong": {
|
||||
value: "{ax.meta-lime.700.value}",
|
||||
value: "{ax.meta-lime.700}",
|
||||
type: "color",
|
||||
group: "border.meta-lime",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const metaPurpleSemanticTokenConfig = {
|
||||
bg: {
|
||||
"meta-purple-soft": {
|
||||
value: "{ax.meta-purple.100.value}",
|
||||
value: "{ax.meta-purple.100}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"meta-purple-softA": {
|
||||
value: "{ax.meta-purple.100A.value}",
|
||||
value: "{ax.meta-purple.100A}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-purple-moderate": {
|
||||
value: "{ax.meta-purple.200.value}",
|
||||
value: "{ax.meta-purple.200}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border.",
|
||||
},
|
||||
"meta-purple-moderateA": {
|
||||
value: "{ax.meta-purple.200A.value}",
|
||||
value: "{ax.meta-purple.200A}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-purple-moderate-hover": {
|
||||
value: "{ax.meta-purple.300.value}",
|
||||
value: "{ax.meta-purple.300}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"meta-purple-moderate-hoverA": {
|
||||
value: "{ax.meta-purple.300A.value}",
|
||||
value: "{ax.meta-purple.300A}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-purple-moderate-pressed": {
|
||||
value: "{ax.meta-purple.400.value}",
|
||||
value: "{ax.meta-purple.400}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"meta-purple-moderate-pressedA": {
|
||||
value: "{ax.meta-purple.400A.value}",
|
||||
value: "{ax.meta-purple.400A}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-purple-strong": {
|
||||
value: "{ax.meta-purple.600.value}",
|
||||
value: "{ax.meta-purple.600}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"meta-purple-strong-hover": {
|
||||
value: "{ax.meta-purple.700.value}",
|
||||
value: "{ax.meta-purple.700}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"meta-purple-strong-pressed": {
|
||||
value: "{ax.meta-purple.800.value}",
|
||||
value: "{ax.meta-purple.800}",
|
||||
type: "color",
|
||||
group: "background.meta-purple",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const metaPurpleSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
"meta-purple": {
|
||||
value: "{ax.meta-purple.1000.value}",
|
||||
value: "{ax.meta-purple.1000}",
|
||||
type: "color",
|
||||
group: "text.meta-purple",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen meta purple. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"meta-purple-subtle": {
|
||||
value: "{ax.meta-purple.800.value}",
|
||||
value: "{ax.meta-purple.800}",
|
||||
type: "color",
|
||||
group: "text.meta-purple",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen meta purple. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"meta-purple-decoration": {
|
||||
value: "{ax.meta-purple.600.value}",
|
||||
value: "{ax.meta-purple.600}",
|
||||
type: "color",
|
||||
group: "text.meta-purple",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"meta-purple-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.meta-purple",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const metaPurpleSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
"meta-purple": {
|
||||
value: "{ax.meta-purple.600.value}",
|
||||
value: "{ax.meta-purple.600}",
|
||||
type: "color",
|
||||
group: "border.meta-purple",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"meta-purple-subtle": {
|
||||
value: "{ax.meta-purple.400.value}",
|
||||
value: "{ax.meta-purple.400}",
|
||||
type: "color",
|
||||
group: "border.meta-purple",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"meta-purple-subtleA": {
|
||||
value: "{ax.meta-purple.400A.value}",
|
||||
value: "{ax.meta-purple.400A}",
|
||||
type: "color",
|
||||
group: "border.meta-purple",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"meta-purple-strong": {
|
||||
value: "{ax.meta-purple.700.value}",
|
||||
value: "{ax.meta-purple.700}",
|
||||
type: "color",
|
||||
group: "border.meta-purple",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const neutralSemanticTokenConfig = {
|
||||
bg: {
|
||||
"neutral-soft": {
|
||||
value: "{ax.neutral.100.value}",
|
||||
value: "{ax.neutral.100}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"neutral-softA": {
|
||||
value: "{ax.neutral.100A.value}",
|
||||
value: "{ax.neutral.100A}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"neutral-moderate": {
|
||||
value: "{ax.neutral.200.value}",
|
||||
value: "{ax.neutral.200}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border.",
|
||||
},
|
||||
"neutral-moderateA": {
|
||||
value: "{ax.neutral.200A.value}",
|
||||
value: "{ax.neutral.200A}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"neutral-moderate-hover": {
|
||||
value: "{ax.neutral.300.value}",
|
||||
value: "{ax.neutral.300}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"neutral-moderate-hoverA": {
|
||||
value: "{ax.neutral.300A.value}",
|
||||
value: "{ax.neutral.300A}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer (f.eks. button neutral secondary hover). Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"neutral-moderate-pressed": {
|
||||
value: "{ax.neutral.400.value}",
|
||||
value: "{ax.neutral.400}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"neutral-moderate-pressedA": {
|
||||
value: "{ax.neutral.400A.value}",
|
||||
value: "{ax.neutral.400A}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer (f.eks. button neutral secondary active). Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"neutral-strong": {
|
||||
value: "{ax.neutral.700.value}",
|
||||
value: "{ax.neutral.700}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer (f.eks. button neutral primary).",
|
||||
},
|
||||
"neutral-strong-hover": {
|
||||
value: "{ax.neutral.800.value}",
|
||||
value: "{ax.neutral.800}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer (f.eks. button neutral primary hover).",
|
||||
},
|
||||
"neutral-strong-pressed": {
|
||||
value: "{ax.neutral.900.value}",
|
||||
value: "{ax.neutral.900}",
|
||||
type: "color",
|
||||
group: "background.neutral",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const neutralSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
neutral: {
|
||||
value: "{ax.neutral.1000.value}",
|
||||
value: "{ax.neutral.1000}",
|
||||
type: "color",
|
||||
group: "text.neutral",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen neutral. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"neutral-subtle": {
|
||||
value: "{ax.neutral.900.value}",
|
||||
value: "{ax.neutral.900}",
|
||||
type: "color",
|
||||
group: "text.neutral",
|
||||
comment:
|
||||
"En nedtonet farge for tekst og ikoner med rollen neutral. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"neutral-decoration": {
|
||||
value: "{ax.neutral.600.value}",
|
||||
value: "{ax.neutral.600}",
|
||||
type: "color",
|
||||
group: "text.neutral",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"neutral-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.neutral",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const neutralSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
neutral: {
|
||||
value: "{ax.neutral.600.value}",
|
||||
value: "{ax.neutral.600}",
|
||||
type: "color",
|
||||
group: "border.neutral",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"neutral-subtle": {
|
||||
value: "{ax.neutral.400.value}",
|
||||
value: "{ax.neutral.400}",
|
||||
type: "color",
|
||||
group: "border.neutral",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"neutral-subtleA": {
|
||||
value: "{ax.neutral.400A.value}",
|
||||
value: "{ax.neutral.400A}",
|
||||
type: "color",
|
||||
group: "border.neutral",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"neutral-strong": {
|
||||
value: "{ax.neutral.700.value}",
|
||||
value: "{ax.neutral.700}",
|
||||
type: "color",
|
||||
group: "border.neutral",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const successSemanticTokenConfig = {
|
||||
bg: {
|
||||
"success-soft": {
|
||||
value: "{ax.success.100.value}",
|
||||
value: "{ax.success.100}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"success-softA": {
|
||||
value: "{ax.success.100A.value}",
|
||||
value: "{ax.success.100A}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"success-moderate": {
|
||||
value: "{ax.success.200.value}",
|
||||
value: "{ax.success.200}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border (f.eks. alert success).",
|
||||
},
|
||||
"success-moderateA": {
|
||||
value: "{ax.success.200A.value}",
|
||||
value: "{ax.success.200A}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"success-moderate-hover": {
|
||||
value: "{ax.success.300.value}",
|
||||
value: "{ax.success.300}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"success-moderate-hoverA": {
|
||||
value: "{ax.success.300A.value}",
|
||||
value: "{ax.success.300A}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"success-moderate-pressed": {
|
||||
value: "{ax.success.400.value}",
|
||||
value: "{ax.success.400}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"success-moderate-pressedA": {
|
||||
value: "{ax.success.400A.value}",
|
||||
value: "{ax.success.400A}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"success-strong": {
|
||||
value: "{ax.success.600.value}",
|
||||
value: "{ax.success.600}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"success-strong-hover": {
|
||||
value: "{ax.success.700.value}",
|
||||
value: "{ax.success.700}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"success-strong-pressed": {
|
||||
value: "{ax.success.800.value}",
|
||||
value: "{ax.success.800}",
|
||||
type: "color",
|
||||
group: "background.success",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const successSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
success: {
|
||||
value: "{ax.success.1000.value}",
|
||||
value: "{ax.success.1000}",
|
||||
type: "color",
|
||||
group: "text.success",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen success. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"success-subtle": {
|
||||
value: "{ax.success.800.value}",
|
||||
value: "{ax.success.800}",
|
||||
type: "color",
|
||||
group: "text.success",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen success. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"success-decoration": {
|
||||
value: "{ax.success.600.value}",
|
||||
value: "{ax.success.600}",
|
||||
type: "color",
|
||||
group: "text.success",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"success-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.success",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const successSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
success: {
|
||||
value: "{ax.success.600.value}",
|
||||
value: "{ax.success.600}",
|
||||
type: "color",
|
||||
group: "border.success",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"success-subtle": {
|
||||
value: "{ax.success.400.value}",
|
||||
value: "{ax.success.400}",
|
||||
type: "color",
|
||||
group: "border.success",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"success-subtleA": {
|
||||
value: "{ax.success.400A.value}",
|
||||
value: "{ax.success.400A}",
|
||||
type: "color",
|
||||
group: "border.success",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"success-strong": {
|
||||
value: "{ax.success.700.value}",
|
||||
value: "{ax.success.700}",
|
||||
type: "color",
|
||||
group: "border.success",
|
||||
comment:
|
||||
|
||||
@@ -3,77 +3,77 @@ import type { StyleDictionaryTokenConfig } from "../../../tokens.util";
|
||||
export const warningSemanticTokenConfig = {
|
||||
bg: {
|
||||
"warning-soft": {
|
||||
value: "{ax.warning.100.value}",
|
||||
value: "{ax.warning.100}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav.",
|
||||
},
|
||||
"warning-softA": {
|
||||
value: "{ax.warning.100A.value}",
|
||||
value: "{ax.warning.100A}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En svak bakgrunnsfarge som kun brukes til dekor. Dekker ingen kontrastkrav. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"warning-moderate": {
|
||||
value: "{ax.warning.200.value}",
|
||||
value: "{ax.warning.200}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border (f.eks. alert warning).",
|
||||
},
|
||||
"warning-moderateA": {
|
||||
value: "{ax.warning.200A.value}",
|
||||
value: "{ax.warning.200A}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til dekor. Om den brukes på meningsbærende elementer må den kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"warning-moderate-hover": {
|
||||
value: "{ax.warning.300.value}",
|
||||
value: "{ax.warning.300}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"warning-moderate-hoverA": {
|
||||
value: "{ax.warning.300A.value}",
|
||||
value: "{ax.warning.300A}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til hover-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"warning-moderate-pressed": {
|
||||
value: "{ax.warning.400.value}",
|
||||
value: "{ax.warning.400}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border.",
|
||||
},
|
||||
"warning-moderate-pressedA": {
|
||||
value: "{ax.warning.400A.value}",
|
||||
value: "{ax.warning.400A}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En medium-svak bakgrunnsfarge som brukes til active/selected-state på meningsbærende elementer. Må kombineres med en godkjent border. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"warning-strong": {
|
||||
value: "{ax.warning.600.value}",
|
||||
value: "{ax.warning.600}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes på meningsbærende elementer.",
|
||||
},
|
||||
"warning-strong-hover": {
|
||||
value: "{ax.warning.700.value}",
|
||||
value: "{ax.warning.700}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
"En sterk bakgrunnsfarge som brukes til hover-state på meningsbærende elementer.",
|
||||
},
|
||||
"warning-strong-pressed": {
|
||||
value: "{ax.warning.800.value}",
|
||||
value: "{ax.warning.800}",
|
||||
type: "color",
|
||||
group: "background.warning",
|
||||
comment:
|
||||
@@ -82,28 +82,28 @@ export const warningSemanticTokenConfig = {
|
||||
},
|
||||
text: {
|
||||
warning: {
|
||||
value: "{ax.warning.1000.value}",
|
||||
value: "{ax.warning.1000}",
|
||||
type: "color",
|
||||
group: "text.warning",
|
||||
comment:
|
||||
"Sterk farge for tekst og ikoner for rollen warning. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"warning-subtle": {
|
||||
value: "{ax.warning.800.value}",
|
||||
value: "{ax.warning.800}",
|
||||
type: "color",
|
||||
group: "text.warning",
|
||||
comment:
|
||||
"Standard farge for tekst og ikoner med rollen warning. Godkjent på alle bakgrunner unntatt strong.",
|
||||
},
|
||||
"warning-decoration": {
|
||||
value: "{ax.warning.600.value}",
|
||||
value: "{ax.warning.600}",
|
||||
type: "color",
|
||||
group: "text.warning",
|
||||
comment:
|
||||
"En farge som kun brukes på ikke-tekstlig innhold (ikoner og andre grafiske elementer). **Ikke godkjent på tekst altså**.",
|
||||
},
|
||||
"warning-contrast": {
|
||||
value: "{ax.neutral.000.value}",
|
||||
value: "{ax.neutral.000}",
|
||||
type: "color",
|
||||
group: "text.warning",
|
||||
comment:
|
||||
@@ -112,27 +112,27 @@ export const warningSemanticTokenConfig = {
|
||||
},
|
||||
border: {
|
||||
warning: {
|
||||
value: "{ax.warning.600.value}",
|
||||
value: "{ax.warning.600}",
|
||||
type: "color",
|
||||
group: "border.warning",
|
||||
comment: "Standard farge for border.",
|
||||
},
|
||||
"warning-subtle": {
|
||||
value: "{ax.warning.400.value}",
|
||||
value: "{ax.warning.400}",
|
||||
type: "color",
|
||||
group: "border.warning",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon.",
|
||||
},
|
||||
"warning-subtleA": {
|
||||
value: "{ax.warning.400A.value}",
|
||||
value: "{ax.warning.400A}",
|
||||
type: "color",
|
||||
group: "border.warning",
|
||||
comment:
|
||||
"En svak border-farge som brukes til dekor. Om den brukes på interaktive elementer må den kombineres med elementer som tydelig signaliserer interaksjon. Er delvis gjennomsiktig.",
|
||||
},
|
||||
"warning-strong": {
|
||||
value: "{ax.warning.700.value}",
|
||||
value: "{ax.warning.700}",
|
||||
type: "color",
|
||||
group: "border.warning",
|
||||
comment:
|
||||
|
||||
@@ -14,7 +14,7 @@ export function semanticRootTokens(theme: AkselColorTheme) {
|
||||
return {
|
||||
text: {
|
||||
logo: {
|
||||
value: theme === "light" ? "#C30000" : "{ax.neutral.1000.value}",
|
||||
value: theme === "light" ? "#C30000" : "{ax.neutral.1000}",
|
||||
type: "color",
|
||||
group: "text",
|
||||
comment: "Navs logo-farge",
|
||||
@@ -43,17 +43,14 @@ export function semanticRootTokens(theme: AkselColorTheme) {
|
||||
"Bakgrunnsfargen som brukes på input-elementer (tekstfelt, sjekkbokser, o.l.) ",
|
||||
},
|
||||
raised: {
|
||||
value:
|
||||
theme === "light"
|
||||
? "{ax.neutral.000.value}"
|
||||
: "{ax.neutral.200.value}",
|
||||
value: theme === "light" ? "{ax.neutral.000}" : "{ax.neutral.200}",
|
||||
type: "color",
|
||||
group: "background",
|
||||
comment:
|
||||
"Bakgrunnsfarge på bokser som svever eller som skal fremheves i darkmode. F.eks. Card.",
|
||||
},
|
||||
sunken: {
|
||||
value: theme === "light" ? "{ax.neutral.200.value}" : "#07090D",
|
||||
value: theme === "light" ? "{ax.neutral.200}" : "#07090D",
|
||||
type: "color",
|
||||
group: "background",
|
||||
comment:
|
||||
@@ -69,7 +66,7 @@ export function semanticRootTokens(theme: AkselColorTheme) {
|
||||
},
|
||||
border: {
|
||||
focus: {
|
||||
value: "{ax.neutral.1000.value}",
|
||||
value: "{ax.neutral.1000}",
|
||||
type: "color",
|
||||
group: "border",
|
||||
/**
|
||||
|
||||
@@ -17,88 +17,91 @@ import type { AkselColorRole } from "../../../types";
|
||||
* --ax-bg-moderate: var(--ax-bg-success-moderate);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* Source for all our color generation: https://www.figma.com/design/NGULm4ljBe9o5xKbl5pJqD/Nye-farger?node-id=785-1106&p=f&t=sJq3Y4e1qfUNKkcI-0
|
||||
*/
|
||||
export function semanticThemedBaseTokens(role: AkselColorRole) {
|
||||
return {
|
||||
bg: {
|
||||
soft: {
|
||||
value: `{ax.bg.${role}-soft.value}`,
|
||||
value: `{ax.bg.${role}-soft}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
softA: {
|
||||
value: `{ax.bg.${role}-softA.value}`,
|
||||
value: `{ax.bg.${role}-softA}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
moderate: {
|
||||
value: `{ax.bg.${role}-moderate.value}`,
|
||||
value: `{ax.bg.${role}-moderate}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
moderateA: {
|
||||
value: `{ax.bg.${role}-moderateA.value}`,
|
||||
value: `{ax.bg.${role}-moderateA}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
"moderate-hover": {
|
||||
value: `{ax.bg.${role}-moderate-hover.value}`,
|
||||
value: `{ax.bg.${role}-moderate-hover}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
"moderate-hoverA": {
|
||||
value: `{ax.bg.${role}-moderate-hoverA.value}`,
|
||||
value: `{ax.bg.${role}-moderate-hoverA}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
"moderate-pressed": {
|
||||
value: `{ax.bg.${role}-moderate-pressed.value}`,
|
||||
value: `{ax.bg.${role}-moderate-pressed}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
"moderate-pressedA": {
|
||||
value: `{ax.bg.${role}-moderate-pressedA.value}`,
|
||||
value: `{ax.bg.${role}-moderate-pressedA}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
strong: {
|
||||
value: `{ax.bg.${role}-strong.value}`,
|
||||
value: `{ax.bg.${role}-strong}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
"strong-hover": {
|
||||
value: `{ax.bg.${role}-strong-hover.value}`,
|
||||
value: `{ax.bg.${role}-strong-hover}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
"strong-pressed": {
|
||||
value: `{ax.bg.${role}-strong-pressed.value}`,
|
||||
value: `{ax.bg.${role}-strong-pressed}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
},
|
||||
text: {
|
||||
default: {
|
||||
value: `{ax.text.${role}.value}`,
|
||||
value: `{ax.text.${role}}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
subtle: {
|
||||
value: `{ax.text.${role}-subtle.value}`,
|
||||
value: `{ax.text.${role}-subtle}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
decoration: {
|
||||
value: `{ax.text.${role}-decoration.value}`,
|
||||
value: `{ax.text.${role}-decoration}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
contrast: {
|
||||
value: `{ax.text.${role}-contrast.value}`,
|
||||
value: `{ax.text.${role}-contrast}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
},
|
||||
border: {
|
||||
default: {
|
||||
value: `{ax.border.${role}.value}`,
|
||||
value: `{ax.border.${role}}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
subtle: {
|
||||
value: `{ax.border.${role}-subtle.value}`,
|
||||
value: `{ax.border.${role}-subtle}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
subtleA: {
|
||||
value: `{ax.border.${role}-subtleA.value}`,
|
||||
value: `{ax.border.${role}-subtleA}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
strong: {
|
||||
value: `{ax.border.${role}-strong.value}`,
|
||||
value: `{ax.border.${role}-strong}`,
|
||||
type: "themed-role",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,55 @@
|
||||
# Changelog
|
||||
|
||||
## 8.19.1
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
- Accordion: `@media print` now auto-opens every Accordion ([#5363](https://github.com/navikt/aksel/pull/5363))
|
||||
|
||||
- ExpansionCard: `@media print` now auto-opens every ExpansionCard ([#5363](https://github.com/navikt/aksel/pull/5363))
|
||||
|
||||
- ReadMore: `@media print` now auto-opens every ReadMore ([#5363](https://github.com/navikt/aksel/pull/5363))
|
||||
|
||||
## 8.19.0
|
||||
|
||||
### @navikt/aksel
|
||||
|
||||
- CLI: Update clipboardy to v5.3.2 ([#5357](https://github.com/navikt/aksel/pull/5357))
|
||||
|
||||
- CLI: Update figlet to 1.11.3 ([#5360](https://github.com/navikt/aksel/pull/5360))
|
||||
|
||||
### @navikt/aksel-icons
|
||||
|
||||
- Icons: Added new icon MenuHamburgerMagnifyingGlass ([#5368](https://github.com/navikt/aksel/pull/5368))
|
||||
|
||||
## 8.18.0
|
||||
|
||||
### @navikt/aksel
|
||||
|
||||
- Codemod: Migrations now run in folders that are not git repositories, and a missing git install gets its own error message. `codemod v8` no longer stops before `v8-tokens` because of changes made by earlier migrations in the same run. ([`470a820`](https://github.com/navikt/aksel/commit/470a8207f23aee71243b986ef2836967c2498e71))
|
||||
|
||||
- Codemod: `v8-tokens` no longer removes whitespace before replaced tokens or rewrites longer names that contain a token name. ([#5322](https://github.com/navikt/aksel/pull/5322))
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
- ActionMenu: Faster and smoother open transition. ([#5296](https://github.com/navikt/aksel/pull/5296))
|
||||
|
||||
### @navikt/ds-react
|
||||
|
||||
- Radio: Add RadioCard and RadioCardGroup components ([#5276](https://github.com/navikt/aksel/pull/5276))
|
||||
|
||||
* Badge, Lookup: now available outside of Preview scope ([#5344](https://github.com/navikt/aksel/pull/5344))
|
||||
|
||||
* DatePicker: Use without hook now correctly closes picker on date-selection ([#5327](https://github.com/navikt/aksel/pull/5327))
|
||||
|
||||
* ActionMenu: Menu is no longer modal, Page scroll and interaction outside the menu are no longer blocked while open. ([#5296](https://github.com/navikt/aksel/pull/5296))
|
||||
|
||||
### @navikt/ds-tailwind
|
||||
|
||||
- v3 preset adds `rounded-ax-*` and no longer lists radius tokens as colors ([#5329](https://github.com/navikt/aksel/pull/5329))
|
||||
|
||||
- v4 theme now uses `@theme inline`, so dark mode and `data-color` work with Tailwind utilities. Font sizes are available as `text-ax-*` and font family as `font-ax-font-family` (`font-family-ax-font-family` is deprecated) ([#5329](https://github.com/navikt/aksel/pull/5329))
|
||||
|
||||
## 8.17.2
|
||||
|
||||
### @navikt/aksel-icons
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ På Windows kan du legge inn tokenet som en lokal miljøvariabel.
|
||||
Nettsiden avhenger av pakker som ikke er åpent tilgjengelig. Du kan likevel bidra på andre deler av repoet ved å kjøre denne kommandoen i stedet for `yarn install`:
|
||||
|
||||
```sh
|
||||
yarn workspaces focus @navikt/aksel-icons @navikt/ds-tokens @navikt/ds-css @navikt/ds-react @navikt/ds-tailwind @navikt/aksel @navikt/aksel-stylelint
|
||||
yarn workspaces focus aksel @navikt/aksel-icons @navikt/ds-tokens @navikt/ds-css @navikt/ds-react @navikt/ds-tailwind @navikt/aksel @navikt/aksel-stylelint
|
||||
```
|
||||
|
||||
## Utviklingsmiljø
|
||||
|
||||
@@ -13,10 +13,6 @@ spec:
|
||||
ingresses:
|
||||
- "https://aksel.nav.no"
|
||||
- "https://design.nav.no"
|
||||
observability:
|
||||
autoInstrumentation:
|
||||
enabled: true
|
||||
runtime: nodejs
|
||||
replicas:
|
||||
disableAutoScaling: true
|
||||
min: 1
|
||||
|
||||
Loaded 100 of 206 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user