Skip to content
Draft
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
0cb9a54
🎉
jpzwarte Mar 16, 2026
eb5e335
🎹
jpzwarte Mar 17, 2026
68dc7b1
🎙
jpzwarte Mar 17, 2026
ff50a1a
🌶
jpzwarte Mar 18, 2026
0a81c3c
🏋
jpzwarte Mar 18, 2026
acb3645
Merge branch 'main' into docs/website-v2
jpzwarte Mar 18, 2026
c1bd9b5
🚢
jpzwarte Mar 19, 2026
43b3719
🚔
jpzwarte Mar 20, 2026
ae6e6cf
Merge branch 'main' into docs/website-v2
jpzwarte Mar 21, 2026
3d55251
🍮
jpzwarte Mar 22, 2026
9eca31b
🎊
jpzwarte Mar 22, 2026
eb42f4b
🚅
jpzwarte Mar 22, 2026
dcf96af
🔈
jpzwarte Mar 22, 2026
8f21736
🌏
jpzwarte Mar 22, 2026
4279d1e
🏹
jpzwarte Mar 22, 2026
b03858a
🐻
jpzwarte Mar 23, 2026
624d730
Merge branch 'main' into docs/website-v2
jpzwarte Mar 23, 2026
222b1a6
📻
jpzwarte Mar 23, 2026
acf5db0
🏇
jpzwarte Mar 23, 2026
51c536b
🏏
jpzwarte Mar 24, 2026
c375aa7
Merge branch 'main' into docs/website-v2
jpzwarte Mar 24, 2026
f378ddc
🐤
jpzwarte Mar 24, 2026
1ffe76c
🌟
jpzwarte Mar 25, 2026
3d7a001
Merge branch 'main' into docs/website-v2
jpzwarte Mar 30, 2026
a87a3ea
🎉
jpzwarte Mar 30, 2026
4bc88ab
🚘
jpzwarte Mar 30, 2026
888a99c
🎖
jpzwarte Mar 31, 2026
165c9a3
Merge branch 'main' into docs/website-v2
jpzwarte Apr 9, 2026
5b538a1
💥
jpzwarte Apr 9, 2026
37d6b67
Merge branch 'main' into docs/website-v2
jpzwarte Apr 13, 2026
26f0856
Merge branch 'main' into docs/website-v2
jpzwarte Apr 13, 2026
53e91a3
🐳
jpzwarte Apr 14, 2026
d73c5c3
jpzwarte Apr 14, 2026
3fc12a6
🐠
jpzwarte Apr 14, 2026
7b2a9f2
⛄️
jpzwarte Apr 14, 2026
0f98d6d
🏇
jpzwarte Apr 14, 2026
c3ef871
💡
jpzwarte Apr 14, 2026
e627fc0
🐩
jpzwarte Apr 14, 2026
cab4d8b
🌏
jpzwarte Apr 14, 2026
dedf662
🌳
jpzwarte Apr 14, 2026
3c38abf
🍵
jpzwarte Apr 14, 2026
ba53164
🏜
jpzwarte Apr 14, 2026
5186700
🎀
jpzwarte Apr 15, 2026
7a1a36c
🚂
jpzwarte Apr 15, 2026
346f2c4
🍡
jpzwarte Apr 15, 2026
fe8f89b
🎪
jpzwarte Apr 15, 2026
39cf4e0
Merge branch 'main' into docs/website-v2
jpzwarte Apr 15, 2026
3fffc5e
📷
jpzwarte Apr 15, 2026
ec18529
☀️
jpzwarte Apr 15, 2026
1cbf739
🍩
jpzwarte Apr 15, 2026
0bf49cc
🚙
jpzwarte Apr 15, 2026
8d000b8
Merge branch 'main' into docs/website-v2
jpzwarte Apr 15, 2026
9e2869d
🏔
jpzwarte Apr 15, 2026
7e5daaf
🏍
jpzwarte Apr 15, 2026
c905ac0
🍕
jpzwarte Apr 15, 2026
d964ec7
🚑
jpzwarte Apr 15, 2026
06502c0
jpzwarte Apr 15, 2026
37ead40
🏐
jpzwarte Apr 16, 2026
8945fce
🏰
jpzwarte Apr 16, 2026
134e06d
🍤
jpzwarte Apr 16, 2026
638f729
ci: run CI + website preview for PRs targeting docs/website-v2
claude Apr 16, 2026
2088061
🚨
jpzwarte Apr 20, 2026
794838f
☕️
jpzwarte Apr 20, 2026
55a20b8
🌝
jpzwarte Apr 20, 2026
2b60679
🍞
jpzwarte Apr 20, 2026
4444d3f
🏋
jpzwarte Apr 21, 2026
a8ad91c
🍗
jpzwarte Apr 21, 2026
891b741
📻
jpzwarte Apr 21, 2026
1b72686
🤘
jpzwarte Apr 21, 2026
d202095
🐈
jpzwarte Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .cem.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
sourceControlRootUrl: 'https://github.com/sl-design-system/components/tree/main'

generate:
files:
- 'packages/components/*/src/**/*.ts'
exclude:
- 'packages/components/*/src/**/*.spec.ts'
- 'packages/components/*/src/**/*.stories.ts'
output: 'custom-elements.json'
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [ "main" ]
branches: [ "main", "docs/website-v2" ]
pull_request:
branches: [ "main" ]
branches: [ "main", "docs/website-v2" ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ on:
pull_request:
branches:
- main
- docs/website-v2
paths-ignore:
- 'packages/tokens/src/**'
types: [opened, synchronize, reopened, closed]

push:
branches:
- main
- docs/website-v2

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand Down Expand Up @@ -47,7 +49,7 @@ jobs:
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: 'website/dist'
path: 'docs/website/dist'
- id: deployment
uses: actions/deploy-pages@v4

Expand All @@ -67,7 +69,7 @@ jobs:
with:
azure_static_web_apps_api_token: ${{ secrets.WEBSITE_AZURE_STATIC_WEB_APPS_API_TOKEN }}
action: upload
app_location: website/dist
app_location: docs/website/dist
production_branch: main
skip_api_build: true
skip_app_build: true
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,5 @@ __snapshots__

# Intellij
.idea/

*storybook.log
39 changes: 39 additions & 0 deletions docs/components/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { importCSSSheet } from '@roenlie/vite-plugin-import-css-sheet'
import { type StorybookConfig } from '@storybook/web-components-vite';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string) {
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)))
}

const config: StorybookConfig = {
stories: [
'../src/**/*.stories.ts'
],
addons: [
getAbsolutePath('@storybook/addon-vitest'),
getAbsolutePath('@storybook/addon-a11y'),
getAbsolutePath('@storybook/addon-docs')
],
core: {
disableTelemetry: true
},
framework: getAbsolutePath('@storybook/web-components-vite'),
staticDirs: [
{ from: '../../../packages/themes', to: '/themes' },
],
async viteFinal(config) {
const { mergeConfig } = await import('vite');

return mergeConfig(config, {
plugins: [importCSSSheet()]
});
}
};

export default config;
10 changes: 10 additions & 0 deletions docs/components/.storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<link rel="stylesheet" href="https://use.typekit.net/kes1hoh.css" />
<link rel="stylesheet" href="/themes/sanoma-learning/global.css" />
<link rel="stylesheet" href="/themes/sanoma-learning/light.css" />
<style>
body {
background: var(--sl-elevation-surface-base-default);
color: var(--sl-color-foreground-plain);
font: var(--sl-text-new-body-md);
}
</style>
25 changes: 25 additions & 0 deletions docs/components/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import '@webcomponents/scoped-custom-element-registry/scoped-custom-element-registry.min.js';
import { setup } from '@sl-design-system/sanoma-learning';
import { type Preview } from '@storybook/web-components-vite';

setup();

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i
}
},

a11y: {
// 'todo' - show a11y violations in the test UI only
// 'error' - fail CI on a11y violations
// 'off' - skip a11y checks entirely
test: 'todo'
}
}
};

export default preview;
7 changes: 7 additions & 0 deletions docs/components/.storybook/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setup file uses double quotes in the import, which will violate the repo’s enforced single-quote rule (@stylistic/quotes) and likely fail lint/format checks. Update to single quotes (and run formatting).

Suggested change
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';

Copilot uses AI. Check for mistakes.
import { setProjectAnnotations } from '@storybook/web-components-vite';
import * as projectAnnotations from './preview';

// This is an important step to apply the right configuration when testing your stories.
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
4 changes: 4 additions & 0 deletions docs/components/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.css' {
const css: CSSStyleSheet;
export default css;
}
88 changes: 88 additions & 0 deletions docs/components/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"name": "@sl-design-system/doc-components",
"private": true,
"type": "module",
"version": "0.0.0",
"description": "Web components used in the documentation of the SL Design System",
"license": "ISC",
"repository": {
"type": "git",
"url": "https://github.com/sl-design-system/components.git"
},
"exports": {
"./code-block/code-block.js": "./dist/code-block/code-block.js",
"./code-example/code-example.js": "./dist/code-example/code-example.js",
"./code/code.js": "./dist/code/code.js",
"./copy-button/copy-button.js": "./dist/copy-button/copy-button.js",
"./heading/heading.js": "./dist/heading/heading.js",
"./install-info/install-info.js": "./dist/install-info/install-info.js",
"./open-issue-count/open-issue-count.js": "./dist/open-issue-count/open-issue-count.js",
"./page-toc/page-toc.js": "./dist/page-toc/page-toc.js",
"./search/search.js": "./dist/search/search.js",
"./sidebar/sidebar.js": "./dist/sidebar/sidebar.js",
"./site-nav/nav-group.js": "./dist/site-nav/nav-group.js",
"./site-nav/nav-item.js": "./dist/site-nav/nav-item.js",
"./site-nav/site-nav.js": "./dist/site-nav/site-nav.js",
Comment on lines +12 to +25
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exports map only exposes paths with a .js suffix (e.g. ./sidebar/sidebar.js), but several docs component tests import extensionless subpaths (e.g. @sl-design-system/doc-components/sidebar/sidebar). With Node/Vite package exports, those imports won't resolve. Either update the tests/consumers to include .js, or add matching extensionless entries to exports for backwards/ergonomic imports.

Suggested change
"exports": {
"./code-example/code-example.js": "./dist/code-example/code-example.js",
"./code/code.js": "./dist/code/code.js",
"./copy-button/copy-button.js": "./dist/copy-button/copy-button.js",
"./heading/heading.js": "./dist/heading/heading.js",
"./install-info/install-info.js": "./dist/install-info/install-info.js",
"./page-toc/page-toc.js": "./dist/page-toc/page-toc.js",
"./search/search.js": "./dist/search/search.js",
"./sidebar/sidebar.js": "./dist/sidebar/sidebar.js",
"./site-nav/nav-group.js": "./dist/site-nav/nav-group.js",
"./site-nav/nav-item.js": "./dist/site-nav/nav-item.js",
"./site-nav/site-nav.js": "./dist/site-nav/site-nav.js",
"exports": {
"./code-example/code-example": "./dist/code-example/code-example.js",
"./code-example/code-example.js": "./dist/code-example/code-example.js",
"./code/code": "./dist/code/code.js",
"./code/code.js": "./dist/code/code.js",
"./copy-button/copy-button": "./dist/copy-button/copy-button.js",
"./copy-button/copy-button.js": "./dist/copy-button/copy-button.js",
"./heading/heading": "./dist/heading/heading.js",
"./heading/heading.js": "./dist/heading/heading.js",
"./install-info/install-info": "./dist/install-info/install-info.js",
"./install-info/install-info.js": "./dist/install-info/install-info.js",
"./page-toc/page-toc": "./dist/page-toc/page-toc.js",
"./page-toc/page-toc.js": "./dist/page-toc/page-toc.js",
"./search/search": "./dist/search/search.js",
"./search/search.js": "./dist/search/search.js",
"./sidebar/sidebar": "./dist/sidebar/sidebar.js",
"./sidebar/sidebar.js": "./dist/sidebar/sidebar.js",
"./site-nav/nav-group": "./dist/site-nav/nav-group.js",
"./site-nav/nav-group.js": "./dist/site-nav/nav-group.js",
"./site-nav/nav-item": "./dist/site-nav/nav-item.js",
"./site-nav/nav-item.js": "./dist/site-nav/nav-item.js",
"./site-nav/site-nav": "./dist/site-nav/site-nav.js",
"./site-nav/site-nav.js": "./dist/site-nav/site-nav.js",
"./theme-switch/theme-switch": "./dist/theme-switch/theme-switch.js",

Copilot uses AI. Check for mistakes.
"./theme-switch/theme-switch.js": "./dist/theme-switch/theme-switch.js",
"./package.json": "./package.json"
},
"scripts": {
"build": "wireit",
"start": "wireit",
"watch": "tsdown --watch"
},
"wireit": {
"build": {
"command": "tsdown",
"dependencies": [
"../..:build"
]
},
"start": {
"command": "storybook dev -p 6009 --no-open",
"service": {
"readyWhen": {
"lineMatches": "Storybook ready!"
}
},
"files": [
".storybook/main.ts"
]
}
},
"dependencies": {
"@open-wc/scoped-elements": "^3.0.6",
"@sl-design-system/icon": "workspace:^",
"@sl-design-system/search-field": "workspace:^",
"@sl-design-system/switch": "workspace:^",
"prismjs": "^1.30.0"
},
"devDependencies": {
"@roenlie/vite-plugin-import-css-sheet": "^0.0.7",
"@storybook/addon-a11y": "^10.3.5",
"@storybook/addon-docs": "^10.3.5",
"@storybook/addon-vitest": "^10.3.5",
"@storybook/web-components": "^10.3.5",
"@storybook/web-components-vite": "^10.3.5",
"@tsdown/css": "^0.21.7",
"@types/prismjs": "^1.26.6",
"@typescript/native-preview": "^7.0.0-dev.20260413.1",
"lit": "^3.3.2",
"sass-embedded": "^1.99.0",
"storybook": "^10.3.5",
"tsdown": "^0.21.7",
"typescript": "^5.9.3",
"wireit": "^0.14.12"
},
"peerDependencies": {
"lit": "^3.3.2"
},
"inlinedDependencies": {
"@floating-ui/core": "1.7.5",
"@floating-ui/dom": "1.7.6",
"@floating-ui/utils": "0.2.11",
"@fortawesome/free-brands-svg-icons": "7.2.0",
"@fortawesome/pro-regular-svg-icons": "7.2.0",
"@fortawesome/pro-solid-svg-icons": "7.2.0"
}
}
27 changes: 27 additions & 0 deletions docs/components/src/code-block/code-block.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
:host {
background: var(--sl-color-background-accent-grey-subtlest);
border-radius: var(--sl-size-borderRadius-default);
display: block;
overflow-x: auto;
padding: var(--sl-size-200);
position: relative;
}

:host(:hover) doc-copy-button,
doc-copy-button:focus-within {
opacity: 1;
}

::slotted(pre) {
background: transparent !important;
margin: 0 !important;
padding: 0 !important;
}

doc-copy-button {
inset-block-start: var(--sl-size-100);
inset-inline-end: var(--sl-size-100);
opacity: 0;
position: absolute;
transition: opacity 0.15s;
}
70 changes: 70 additions & 0 deletions docs/components/src/code-block/code-block.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { fixture } from '@sl-design-system/vitest-browser-lit';
import { html } from 'lit';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Code } from './code-block.js';

try {
customElements.define('doc-code-block', Code);
} catch {
/* empty */
}

describe('doc-code-block', () => {
let el: Code;

describe('defaults', () => {
beforeEach(async () => {
el = await fixture(html`
<doc-code-block>
<pre><code>const foo = 'bar';</code></pre>
</doc-code-block>
`);
});

it('should render', () => {
expect(el).to.exist;
expect(el).to.be.instanceOf(Code);
});

it('should render a default slot', () => {
const slot = el.renderRoot.querySelector<HTMLSlotElement>('slot:not([name])');

expect(slot).to.exist;
expect(slot?.assignedElements()).to.have.length.greaterThan(0);
});

it('should render a copy button', () => {
expect(el.renderRoot.querySelector('doc-copy-button')).to.exist;
});

it('should set the copy button content from the slotted pre element', () => {
const copyButton = el.renderRoot.querySelector<HTMLElement & { content?: string }>('doc-copy-button');

expect(copyButton?.content).to.equal("const foo = 'bar';");
});

it('should copy the source to the clipboard on click', async () => {
const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue(undefined);

const copyButtonHost = el.renderRoot.querySelector('doc-copy-button')! as Element & { renderRoot: ShadowRoot };
copyButtonHost.renderRoot.querySelector<HTMLElement>('sl-button')!.click();
await el.updateComplete;

expect(writeText).toHaveBeenCalledWith("const foo = 'bar';");

writeText.mockRestore();
});
});

describe('without pre element', () => {
beforeEach(async () => {
el = await fixture(html`<doc-code-block><span>some text</span></doc-code-block>`);
});

it('should render a copy button with no content', () => {
const copyButton = el.renderRoot.querySelector<HTMLElement & { content?: string }>('doc-copy-button');

expect(copyButton?.content).to.equal('');
});
});
});
48 changes: 48 additions & 0 deletions docs/components/src/code-block/code-block.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { type Meta, type StoryObj } from '@storybook/web-components-vite';
import { html } from 'lit';
import { Code } from './code-block.js';

type Story = StoryObj;

try {
customElements.define('doc-code-block', Code);
} catch {
/* empty */
}

export default {
title: 'Code',
render: () => html`
<doc-code-block>
<pre><code class="language-typescript">import { LitElement, html } from 'lit';

export class MyElement extends LitElement {
override render() {
return html\`&lt;p&gt;Hello world!&lt;/p&gt;\`;
}
}</code></pre>
</doc-code-block>
`
} satisfies Meta;

export const Basic: Story = {};

export const MultiLine: Story = {
render: () => html`
<doc-code-block>
<pre><code class="language-html">&lt;sl-button variant="primary"&gt;Click me&lt;/sl-button&gt;
&lt;sl-button variant="default"&gt;Cancel&lt;/sl-button&gt;</code></pre>
</doc-code-block>
`
};

export const CSS: Story = {
render: () => html`
<doc-code-block>
<pre><code class="language-css">:host {
display: block;
color: red;
}</code></pre>
</doc-code-block>
`
};
Loading
Loading