Skip to content

Commit 94bd50a

Browse files
authored
docs: restructure /components URLs (#4260)
1 parent 13bb269 commit 94bd50a

11 files changed

Lines changed: 394 additions & 99 deletions

File tree

File renamed without changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: Get started with React
3+
sidebar_title: React
4+
order: 40
5+
---
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
title: Kom i gang med CSS
3+
sidebar_title: CSS
4+
order: 50
5+
---
6+
7+
# Bruk
8+
9+
På denne siden finner du informasjon om hvordan du kan bruke Designsystemet.
10+
Under kan du lese om:
11+
12+
For å bruke Designsystemets CSS, så må du installere `@digdir/designsystemet-css` og `@digdir/designsystemet-theme`.
13+
14+
### Layers
15+
16+
Vi bruker [css layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) for å få lavest mulig spesifisitet på vår css.
17+
Dette betyr at du lett kan overstye med din egen styling.
18+
19+
Alle våre layers ligger under layer gruppen `ds`.
20+
21+
Har du f.eks en css reset, så vil denne overstyre deler av det vi har gjort. Vi anbefaler å legge denne i en layer og sortere denne først.
22+
Dette gjør du ved å først legge reseten din i en layer, og så sortere denne layeren først:
23+
24+
```css
25+
@layer my-reset, ds;
26+
```
27+
28+
### Data attributter
29+
30+
Vi tilbyr flere data attributter som kan brukes for å endre tema på komponentene.
31+
32+
#### `data-color-scheme`
33+
34+
Brukes til å endre fargemodus på komponentene. Mulige verdier er `light`, `dark` og `auto`.
35+
Dersom `auto` er valgt, vil fargemodusen endres basert på brukerens preferanser ved hjelp av
36+
[prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
37+
38+
#### `data-typography`
39+
40+
Brukes til å endre typografien på komponentene. Mulige verdier er `primary` og `secondary`.
41+
Et vanlig bruksområde for denne attributten er når du ønsker en monospace font på tabeller.
42+
43+
Hvilken typografi som er tilknyttet `primary` eller `secondary` må defineres i teamet, ved hjelp av [Tokens Studio](https://tokens.studio/)
44+
(med Designsystemet design-tokens) eller [Temabyggeren](https://theme.designsystemet.no/).
45+
46+
Dersom du skal bruke denne attributten må du huske å definere `font-family` til å bruke `--ds-font-family` CSS variabelen.
47+
Dette kan løses ved å f.eks bruke [attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) i CSS.
48+
49+
```html
50+
<style>
51+
[data-typography='secondary'] {
52+
font-family: var(--ds-font-family), monospace;
53+
}
54+
</style>
55+
<table data-typography='secondary'>
56+
57+
</table>
58+
```
59+
60+
### Hjelpeklasser
61+
62+
Vi benytter oss av enkelte hjelpeklasser som kan brukes mer generelt på tvers av komponenter.
63+
64+
#### `ds-sr-only`
65+
Skjuler et element visuelt mens det fortsatt er tilgjengelig for skjermlesere.
66+
67+
#### `ds-print-preserve`
68+
Bevarer utseende, inkludert bakgrunnsfarge, i print.
69+
70+
#### `ds-focus` / `ds-focus--inset`
71+
Legger til designsystemets fokusring rundt elementet _når det får fokus med tastaturnavigering_ (`:focus-visible`).
72+
73+
#### `ds-focus--visible` / `ds-focus--visible--inset`
74+
Statiske "alltid på" klasser som legger til designsystemets fokusring rundt elementet. <span className='ds-focus--visible'>Eksempel.</span>
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
title: Kom i gang med React
3+
sidebar_title: React
4+
order: 40
5+
---
6+
7+
Følg disse stegene for å komme i gang med React-komponentene.
8+
9+
## 1. Installer pakkene
10+
11+
```sh
12+
npm i @digdir/designsystemet-react @digdir/designsystemet-theme @digdir/designsystemet-css @digdir/designsystemet-types
13+
```
14+
15+
### Typescript
16+
17+
Dersom du bruker Typescript, sørg for at du har typescript >= 3.8 i `devDependencies`:
18+
19+
```sh
20+
npm i typescript --save-dev
21+
```
22+
23+
## 2. Font
24+
25+
Du kan fritt bruke hvilken som helst font-familie med komponentene.
26+
27+
Komponentene er designet og utviklet med [Inter fonten](https://github.com/rsms/inter), så variasjoner kan forekomme dersom du bruker en annen font.
28+
29+
### Legg til Inter fonten (valgfritt)
30+
31+
Legg til `<link>` taggen i `<head>`, og sett `font-family` til `Inter` i din globale css fil.
32+
33+
`font-feature-settings` legger til en hale på små `L`'er.
34+
35+
#### HTML
36+
37+
Du kan bruke denne koden for å legge til fonten:
38+
39+
```html
40+
<link
41+
rel="stylesheet"
42+
href="https://altinncdn.no/fonts/inter/v4.1/inter.css"
43+
integrity="sha384-OcHzc/By/OPw9uJREawUCjP2inbOGKtKb4A/I2iXxmknUfog2H8Adx71tWVZRscD"
44+
crossorigin="anonymous"
45+
/>
46+
```
47+
48+
#### CSS
49+
50+
Vi anbefaler sterkt at du legger til følgende i din globale css fil.
51+
`font-feature-settings` legger til en hale på små `L`'er.
52+
53+
```css
54+
body {
55+
font-family: 'Inter', sans-serif;
56+
font-feature-settings: 'cv05' 1; /* Enable lowercase l with tail */
57+
}
58+
```
59+
60+
Dersom du velger å installere fonten på en annen måte, husk å inkludere fontvektene `400`, `500` og `600`.
61+
62+
## 3. Polyfill
63+
64+
Designsystemet bruker [popover](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover) i enkelte komponenter. Dette apiet er klassifisert som [Baseline: Newly available](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility) fra april 2024, da Firefox som siste nettleser la det til. I noen tilfeller kan en oppleve at brukere av ulike grunner er låst til eldre nettleserversjoner, og da kan det være aktuelt å legge til en polyfill for å sikre at `popover` fungerer for alle.
65+
* [Popover-Polyfill](https://github.com/oddbird/popover-polyfill)
66+
67+
## 4. Bruk en React-komponent
68+
69+
```jsx
70+
import '@digdir/designsystemet-theme';
71+
import '@digdir/designsystemet-css';
72+
73+
import { Button } from '@digdir/designsystemet-react';
74+
75+
<Button variant='secondary'>Jeg er en knapp!</Button>;
76+
```
77+
78+
`@digdir/designsystemet-theme` og `@digdir/designsystemet-css` må kun importeres én gang i applikasjonen.

apps/www/app/layouts/components/layout.tsx

Lines changed: 92 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,16 @@ import { Outlet } from 'react-router';
44
import { Sidebar } from '~/_components/sidebar/sidebar';
55
import {
66
getFileFromContentDir,
7+
getFilesFromContentDir,
78
getFoldersInContentDir,
89
} from '~/_utils/files.server';
10+
import { generateFromMdx } from '~/_utils/generate-from-mdx';
911
import i18n from '~/i18next.server';
1012
import type { Route } from './+types/layout';
1113
import classes from './layout.module.css';
1214

1315
export { ErrorBoundary } from '~/root';
1416

15-
// Maps to store unique entries per category
16-
const categoryMaps = new Map<
17-
string,
18-
Map<string, { title: string; url: string }>
19-
>();
20-
const getStartedMap = new Map<string, { title: string; url: string }>();
21-
22-
const cats: {
23-
[key: string]: {
24-
title: string;
25-
url: string;
26-
}[];
27-
} = {
28-
getStarted: [],
29-
};
30-
3117
export const loader = async ({
3218
params: { lang },
3319
request,
@@ -45,77 +31,110 @@ export const loader = async ({
4531

4632
const t = await i18n.getFixedT(lang);
4733

48-
if (!cats.getStarted.length) {
49-
categoryMaps.clear();
50-
getStartedMap.clear();
51-
52-
/* read all folders in content/components */
53-
const folders = getFoldersInContentDir('/components');
54-
55-
await Promise.all(
56-
folders.map(async (folder) => {
57-
const metadataJson = getFileFromContentDir(
58-
join('components', folder, 'metadata.json'),
59-
);
60-
61-
if (!metadataJson) {
62-
const category = 'components';
63-
if (!categoryMaps.has(category)) {
64-
categoryMaps.set(category, new Map());
65-
}
66-
const categoryMap = categoryMaps.get(category);
67-
if (categoryMap) {
68-
categoryMap.set(`/${lang}/components/${folder}`, {
69-
title: folder,
70-
url: `/${lang}/components/${folder}`,
71-
});
72-
}
73-
return;
74-
}
75-
76-
const parsedMetadata = JSON.parse(metadataJson);
77-
const category = parsedMetadata.category || 'components';
78-
79-
const component = {
80-
title: parsedMetadata[lang].title || folder,
81-
url: `/${lang}/components/${folder}`,
82-
};
83-
84-
if (!categoryMaps.has(category)) {
85-
categoryMaps.set(category, new Map());
86-
}
87-
const categoryMap = categoryMaps.get(category);
88-
if (categoryMap) {
89-
categoryMap.set(component.url, component);
90-
}
91-
}),
92-
);
34+
const cats: {
35+
getStarted: {
36+
title: string;
37+
url: string;
38+
order: number;
39+
}[];
40+
[key: string]: {
41+
title: string;
42+
url: string;
43+
}[];
44+
} = {
45+
getStarted: [],
46+
};
9347

94-
getStartedMap.set(`/${lang}/changelog`, {
48+
// Get started items (added first)
49+
const getStartedItems = [
50+
{
9551
title: t('components.changelog.title'),
96-
url: `/${lang}/changelog`,
52+
url: `/${lang}/components/changelog`,
53+
order: 1,
54+
},
55+
];
56+
57+
const getStartedFolders = getFilesFromContentDir(
58+
join('components-docs', lang),
59+
);
60+
61+
for (const file of getStartedFolders) {
62+
const fileContent = getFileFromContentDir(
63+
join('components-docs', lang, `${file.relativePath}`),
64+
);
65+
const result = await generateFromMdx(fileContent);
66+
67+
getStartedItems.push({
68+
title:
69+
result.frontmatter.sidebar_title ||
70+
file.relativePath.replace('.mdx', ''),
71+
url: `/${lang}/components/${file.relativePath.replace('.mdx', '')}`,
72+
order: parseInt(result.frontmatter.order, 10) || 9999,
9773
});
74+
}
9875

99-
for (const [category, map] of categoryMaps.entries()) {
100-
cats[category] = Array.from(map.values()).sort((a, b) =>
101-
a.title.localeCompare(b.title),
76+
cats.getStarted = getStartedItems;
77+
78+
/* read all folders in content/components */
79+
const folders = getFoldersInContentDir('/components');
80+
81+
const components = await Promise.all(
82+
folders.map(async (folder) => {
83+
const metadataJson = getFileFromContentDir(
84+
join('components', folder, 'metadata.json'),
10285
);
86+
87+
if (!metadataJson) {
88+
return {
89+
category: 'components',
90+
title: folder,
91+
url: `/${lang}/components/docs/${folder}`,
92+
};
93+
}
94+
95+
const parsedMetadata = JSON.parse(metadataJson);
96+
const category = parsedMetadata.category || 'components';
97+
98+
return {
99+
category,
100+
title: parsedMetadata[lang].title || folder,
101+
url: `/${lang}/components/docs/${folder}`,
102+
};
103+
}),
104+
);
105+
106+
// Group components by category
107+
const componentCategories = new Set<string>();
108+
for (const component of components) {
109+
const { category, ...item } = component;
110+
componentCategories.add(category);
111+
if (!cats[category]) {
112+
cats[category] = [];
103113
}
104-
cats.getStarted = Array.from(getStartedMap.values());
114+
cats[category].push(item);
115+
}
116+
117+
// Sort each category
118+
for (const category in cats) {
119+
cats[category].sort((a, b) => a.title.localeCompare(b.title));
105120
}
106121

107122
const trimmedUrl = request.url.endsWith('/')
108-
? request.url.slice(0, -1)
109-
: request.url;
110-
const compPage = trimmedUrl.split('/').pop();
123+
? request.url.slice(0, -1).split('/')
124+
: request.url.split('/');
125+
const compPage = trimmedUrl[trimmedUrl.length - 1];
111126

112-
const isComponentPage = request.url.includes('/components/');
127+
const isComponentPage = request.url.includes('/components/docs/');
113128

114129
const sidebarSuffix: { [key: string]: string } = {};
115-
for (const category of categoryMaps.keys()) {
116-
sidebarSuffix[category] = isComponentPage ? '/' + compPage : '/overview';
130+
for (const category of componentCategories) {
131+
sidebarSuffix[category] = isComponentPage ? `/${compPage}` : '/overview';
117132
}
118133

134+
cats.getStarted.sort((a, b) => {
135+
return a.order - b.order;
136+
});
137+
119138
return {
120139
lang,
121140
cats,

apps/www/app/routes.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,18 @@ export default [
6262
id: 'components',
6363
}),
6464
layout('./layouts/components/layout.tsx', [
65-
route('/changelog', 'routes/components/changelog.tsx', {
66-
id: 'changelog-page',
67-
}),
6865
...prefix('/components', [
69-
...prefix(':component', [
66+
...prefix('/docs/:component', [
7067
route('/*', 'routes/components/component.tsx', {
7168
id: 'components-page',
7269
}),
7370
]),
71+
route('/changelog', 'routes/components/changelog.tsx', {
72+
id: 'changelog-page',
73+
}),
74+
route('/*', 'routes/components/text.tsx', {
75+
id: 'component-text-page',
76+
}),
7477
]),
7578
]),
7679
route('*', 'routes/not-found.tsx', {

apps/www/app/routes/components/changelog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import classes from './component.module.css';
1414
export async function loader({ params }: Route.LoaderArgs) {
1515
// Read the file content
1616
const fileContent = getFileFromContentDir(
17-
join('changelogs', `changelog.mdx`),
17+
join('components-docs', `changelog.mdx`),
1818
);
1919

2020
if (!fileContent) {

0 commit comments

Comments
 (0)