# Yle Design System (YDS) — Complete Documentation
---
# Get started
# For AI agents
Coding agents such as Claude Code and Cursor can build with YDS: they read the documentation as Markdown and install the same packages developers do. Agents do best when the project tells them where the docs are, which rules to follow and how the app is set up. This page collects the techniques that make that work, most of them from the YDS Agentic Proto Kit.
## Start from the proto kit
The [YDS Agentic Proto Kit](https://github.com/Yleisradio/yds-agentic-proto-kit) is a starter project for prototyping with YDS and AI agents: React, TypeScript and Vite, with everything on this page already in place.
- YDS 3.0 set up: tokens on `:root`, cascade layers, components, icons, infographics and custom themes
- A copy of the YDS docs in `.yds-docs/`
- Agent instructions in `AGENTS.md`, shared by Claude Code and Cursor
- Skills for building UI, charts and custom themes
```bash
cp .env.example .env.local # then add your GitHub token
npm run setup
npm run dev
```
The token is the same one developers use: see [Install the packages](#install-the-packages).
## Read the documentation
| File | Contents |
| --- | --- |
| [`/llms.txt`](/llms.txt) | Index of every page, with links to the Markdown versions. Follows the [llms.txt](https://llmstxt.org/) convention. |
| [`/llm/…`](/llm/components/Button/Button.md) | One page as Markdown, for example `/llm/components/Button/Button.md` |
| [`/llms-full.txt`](/llms-full.txt) | Every page in one file |
Start from `llms.txt` and fetch only the pages the task needs. `llms-full.txt` is around 800 KB, more than most agents can hold in context at once.
The Markdown versions keep the page content and replace the interactive parts:
- **Component previews** become a *Live preview* link to the page on this site. Code examples stay as code blocks.
- **Token tables** list the CSS custom property and the JavaScript constant for each token, for example `--yds-spacing-16` and `spacing.SPACING_16`.
- **Figma and Storybook links** stay at the top of the page.
## Keep a copy of the docs in the project
Agents read local files faster and more reliably than they fetch URLs. A copy in the project also keeps the docs in step with the package versions you use. This downloads every page and the index into `.yds-docs/`:
```bash
BASE=https://design-system.yle.fi
mkdir -p .yds-docs
curl -fsSL "$BASE/llm/README.md" -o .yds-docs/README.md
curl -fsSL "$BASE/llms.txt" | grep -o '/llm/[^)]*\.md' | while read -r page; do
file=".yds-docs/${page#/llm/}"
mkdir -p "$(dirname "$file")"
curl -fsSL "$BASE$page" -o "$file"
done
```
Run it again when you upgrade YDS packages.
## Write the agent instructions
### One file for every tool
Keep the rules in `AGENTS.md` and point the other tools to it, so they never drift apart:
- **Claude Code:** `CLAUDE.md` contains a single line, `@AGENTS.md`, which imports the file.
- **Cursor:** a rule in `.cursor/rules/` applies to `**/*.tsx` files and refers to `AGENTS.md` for the details.
### Make the agent look things up
Without instructions, agents guess component APIs from general React knowledge. Tell the agent to read the docs first, and give it the index so it knows which pages exist:
```md
## YDS documentation index
> Prefer retrieval-led reasoning over pre-training-led reasoning for any YDS task.
> Read the relevant file from `.yds-docs/` instead of relying on training data.
- [Button](.yds-docs/components/Button/Button.md)
- …
```
The proto kit fills the index automatically: its docs script copies `.yds-docs/README.md` between the markers and rewrites the links to point into `.yds-docs/`.
### State the rules, with wrong and right examples
Short rules with a wrong and a right example work better than long explanations. The rules that matter most:
- **Use YDS components** from `@yleisradio/yds-components-react` instead of custom HTML for anything YDS already provides.
- **Use tokens, never hardcoded values.** Token values already include units, so don't append `px`.
- **Use semantic theme colours.** `var(--yds-color-text-default)` follows the theme; `var(--yds-color-gray-95)` doesn't. Don't import `lightTheme` or `darkTheme` for UI colours either: they're literal hex values. In TypeScript, `defaultTheme.TEXT_DEFAULT` is the typed equivalent of the CSS variable.
- **No 2.x patterns.** Don't read `theme.yds` or add a `styled.d.ts` with `yds`, `ydsThemeName` or `ydsThemeProps`; they don't exist in 3.0.
- **Icons come from** `@yleisradio/yds-icons-react`. Point the agent to the icon list in the docs.
- **Say what to do when YDS has no match:** build it from YDS tokens. The proto kit also allows `lucide-react`, sparingly, for missing icons.
- **Translate Figma designs, don't copy them.** Code from the Figma MCP server is a reference: map its components and variables to YDS components and tokens.
```css
/* WRONG */
.title {
color: #1a1a1a;
font-size: 24px;
padding: 16px;
}
/* RIGHT */
.title {
color: var(--yds-color-text-default);
font-size: var(--yds-typography-default-2xl-bold-font-size);
font-weight: var(--yds-typography-default-2xl-bold-font-weight);
line-height: var(--yds-typography-default-2xl-bold-line-height);
padding: var(--yds-spacing-16);
}
```
### Load detailed workflows only when needed
Keep `AGENTS.md` short and move task-specific detail into skills that the agent loads when the task calls for them. The proto kit has three, for both Claude Code and Cursor:
| Skill | When it loads |
| --- | --- |
| `use-yds` | Building a screen, page or component: checking the docs, picking components, styling with CSS Modules, verifying the build |
| `use-yds-infographics` | Charts and weather icons: required CSS imports, chart props and data format, the icon catalogue |
| `use-yds-theme-custom` | Product or brand themes: generating light and dark themes from two colours with `@yleisradio/yds-theme-custom` |
## Work from Figma designs
Connect the [Figma MCP server](https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Figma-MCP-server) to the agent and give it links to Figma frames. The agent then reads the layout, components and variables directly instead of guessing from a screenshot. This works best when the design uses the YDS Figma libraries, because its components and variables map to YDS components and tokens in code.
The server returns generic React code, so keep the "translate, don't copy" rule above in the agent instructions. [Prototyping with YDS and AI](./for-designers.md#prototyping-with-yds-and-ai) has the step-by-step version for designers.
## Set the app up the way YDS expects
Agents get the YDS 3.0 setup wrong most often in the same few places. Have the project set up correctly from the start, and repeat these points in the instructions:
- **Tokens on `:root`:** one CSS file imports the token CSS from `@yleisradio/yds-core`. See [Put tokens on `:root`](../using-yds/development/get-started-with-v3.md#3-put-tokens-on-root).
- **Layer order first:** declare `@layer reset, yds, app;` before any component CSS loads, and put app styles in `@layer app`. An unlayered reset strips spacing from components.
- **No app-wide theme provider:** `YdsThemeProvider` themes a subtree. Use it only for a nested or custom theme.
- **Extend components with `className`:** never target their internal class names or use `!important`.
- **Infographics:** import `@yleisradio/yds-infographic-components-react/style.css` and the infographic token CSS, or charts render unstyled.
```css
/* global.css, imported first */
@layer reset, yds, app;
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
}
}
```
```tsx
// main.tsx
import './global.css';
import './yds-tokens.css';
import { ToastProvider, ToastNotifications } from '@yleisradio/yds-components-react';
;
```
Bundler settings, Vite included, are in [Get started with 3.0](../using-yds/development/get-started-with-v3.md#4-let-the-bundler-compile-css-modules).
## Install the packages
Agents need the same GitHub Packages access as developers: see [Get access to the packages](./for-developers.md#get-access-to-the-packages). Keep the token where the agent can't read it:
- Store the token in `.env.local` and generate `.npmrc` from it with a setup script, as the proto kit does. Both files are gitignored.
- List both files in `.claudeignore` and `.cursorignore`, so the agent never reads the token into its context.
- In Docker builds, pass the token as a [build secret](https://docs.docker.com/build/building/secrets/) so it isn't stored in an image layer.
## Let the agent check its work
- Ask the agent to run the type check and lint after every change. The `use-yds` skill ends with `npm run build` and `npm run lint`.
- In Claude Code, allow the project's own scripts (`npm run dev`, `build`, `lint`, `format`) in `.claude/settings.json`, so the agent can run them without asking each time.
- Add the dev server to `.claude/launch.json`, so the agent can open the app in a preview and look at the result.
# For designers
There are two ways to design with YDS. Both use the same components and tokens, so the result stays consistent with Yle's services either way. Choose whichever suits the task, or combine them.
- **[Using YDS in Figma](#using-yds-in-figma):** detailed UI design, design reviews and handoff. You work with the YDS Figma libraries and produce a design file.
- **[Prototyping with YDS and AI](#prototyping-with-yds-and-ai):** trying out ideas quickly, interactive flows and real data. An AI coding agent builds a working prototype from YDS components.
## Using YDS in Figma
The YDS Figma libraries contain the same components, tokens and icons that the code packages implement. A design built from them can be implemented with existing components.
### Enable the libraries
1. Open a design file in Yle's Figma.
2. In the **Assets** panel, open **Libraries**.
3. Enable the libraries you need:
- [YDS Foundation](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation): tokens, light and dark themes, text styles and components. Needed for all UI work.
- [YDS Global Colors](https://www.figma.com/design/3ARTAZ2XrJHPq2g8iSBORW/YDS-Global-Colors): global palettes and colour swatches.
- [YDS Icons and Logos](https://www.figma.com/design/lSboxfILj3Thr1o145POJv/YDS-Icons-and-Logos): basic icons, illustrative icons, logos and graphical assets.
- [YDS Infographics](https://www.figma.com/design/ttv0a97s8ViuxBHtRyn3n2/YDS-Infographics): colour themes, weather symbols and examples of simple infographic components.
The Yle Next font is available in Yle's Figma without installing anything. Use the YDS text styles instead of setting font properties by hand.
If you can't open the libraries, ask on the [#design-system](https://yle.slack.com/archives/C01KDJ7JX5Y) Slack channel.
### Design with tokens
- **Use theme colours for UI.** Theme colours are semantic, grouped by purpose: background, text, action, border, feedback and so on. They have a light and a dark value, so a design built with them works in both themes. Use the global palette only when no theme colour fits. See [Theme colors](../design-tokens/theme-colors.md).
- **Space with the spacing scale.** Spacing follows an 8 px rhythm, with 2 px and 4 px for fine adjustments inside components. See [Spacing](../design-tokens/spacing.md).
- **Use text styles for typography.** See [Typography](../design-tokens/typography.md).
- **Check the themes the service uses.** Yle services use both light and dark themes; Yle Areena uses only the dark theme.
### Accessibility checks
YDS colour pairings meet WCAG 2.2 AA contrast when used as documented. When a design combines colours in other ways, check the contrast yourself, for example with a contrast plugin such as Able. Colour-vision simulation, for example with the Color Blind plugin, shows whether information depends on colour alone.
### Handing off to developers
- Every component and token page on this site links to its Figma node and Storybook story at the top.
- Refer to components by the names used on this site; code uses the same names.
- For animation, describe the motion with the transition tokens. See [Motion](../design-principles/motion-language-principles.md) and [Transitions](../design-tokens/transitions.md).
## Prototyping with YDS and AI
An AI coding agent such as Claude Code or Cursor can build a working prototype from real YDS components while you describe what you want in plain language. The prototype looks and behaves like a Yle service, runs in the browser, and can use real data.
### Get started
1. Get a copy of the [YDS Agentic Proto Kit](https://github.com/Yleisradio/yds-agentic-proto-kit). It comes with the agent instructions that keep the agent on YDS components and tokens.
2. Create a GitHub token for installing the YDS packages. The proto kit has a [step-by-step guide](https://github.com/Yleisradio/yds-agentic-proto-kit/blob/main/docs/github-npm-token.md).
3. Copy `.env.example` to `.env.local` and paste the token there yourself. The agent is set up not to read that file.
4. Open the project in your AI coding tool and ask it to run `npm run setup` and start the dev server.
[For AI agents](./for-ai-agents.md) explains how the proto kit works and how to set up your own project the same way.
### Start from a Figma design
For the best results, connect the [Figma MCP server](https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Figma-MCP-server) to your AI coding tool. The agent can then read a frame straight from Figma, with its layout, components and variables, instead of working from a written description or a screenshot.
1. Set up the Figma MCP server in your AI coding tool. Figma recommends the remote server.
2. Design or sketch the screen with the YDS libraries. Use YDS components and variables rather than detached copies and hand-picked values, so the agent can tell which YDS component and token each part is.
3. Copy the link to the frame and give it to the agent in the proto kit, for example: *"Build this screen with YDS components and tokens:"* followed by the link.
The Figma MCP server returns generic React code as a reference. Ask the agent to rebuild the screen with YDS components and tokens instead of copying that code's markup and values.
### Tips for prompting
- **Name the components.** "A list of ChoiceButtons with a primary Button below" gets closer than "some options and a submit button". The [Components](../components/index.md) page shows what is available.
- **Check both themes** and try the prototype with a keyboard. The components are accessible, but how they are combined is up to the prototype.
- **Ask the agent to explain** which components and tokens it used, if you plan to hand the idea off as a design.
## Before designing something new
Whichever way you use YDS:
1. Check [Components](../components/index.md) or the YDS Foundation library in Figma. An existing component, or a variant of one, may already cover the need.
2. Check the [project board](https://github.com/orgs/Yleisradio/projects/32) and [open issues](https://github.com/Yleisradio/yle-design-system/issues) to see if what you need is in progress.
3. Think beyond your own service: would other services need this too? Write down the use cases.
4. If something is missing from YDS: Propose it. See [Support and contributing](./support.md).
# For developers
YDS ships as npm packages for web and React Native, plus static files for native apps. See [Overview](./overview.md#packages) for the full package list.
## Get access to the packages
The packages are published to Yle's GitHub Packages registry, so installing them needs a GitHub account with access to the Yleisradio organisation.
1. Point the `@yleisradio` scope to GitHub Packages in the project's `.npmrc`:
```ini
@yleisradio:registry=https://npm.pkg.github.com/
```
2. Create a [personal access token (classic)](https://github.com/settings/tokens) with the `read:packages` scope. Fine-grained tokens don't work with the npm registry.
3. Authorise the token for the Yleisradio organisation: in the token list, select **Configure SSO** and **Authorize** next to *yleisradio*. Without this step, installs fail with `401 Unauthorized`.
4. [Authenticate npm with the token](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#authenticating-with-a-personal-access-token), for example in `.npmrc`:
```ini
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
```
Keep the token itself out of version control.
5. Give the same access to every environment that installs packages: CI, deployment builds and AI agent sandboxes. In GitHub Actions:
```yaml
- uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: https://npm.pkg.github.com/
scope: '@yleisradio'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
## Choose your starting point
| You're building | Start here |
| --- | --- |
| A React web app | [Get started with yds-components-react 3.0](../using-yds/development/get-started-with-v3.md) |
| A web app without React (Vue, Svelte, plain HTML) | [Without React](../using-yds/development/get-started-with-v3.md#without-react) |
| Your own styles with YDS tokens | [Using tokens](../design-tokens/index.md#using-tokens) |
| Icons only | [Icons and Logos](../icons-and-logos/index.md#installation) |
| A React Native app | [React Native](#react-native) |
| A native iOS or Android app | [Native apps](#native-apps) |
| Charts and infographics | [Infographics](#infographics) |
Every web setup loads the Yle Next font: see [Load Yle Next](../using-yds/development/get-started-with-v3.md#2-load-yle-next).
## React Native
```bash
npm install @yleisradio/yds-core @yleisradio/yds-components-react-native @yleisradio/yds-icons-react-native react-native-svg
```
Wrap the component tree in the theme provider:
```tsx
import { YdsThemeProvider, Tag } from '@yleisradio/yds-components-react-native';
Hello world;
```
Components available for React Native are tagged **React Native** on the [Components](../components/index.md) page.
## Native apps
YDS doesn't provide native iOS or Android components, but native apps can use its assets:
- **Icons:** archives of basic and illustrative icons, SVG for iOS and VectorDrawable for Android. See [CDN](./overview.md#cdn).
- **Colours:** Android colour resources (`colors.xml`, `theme-dark.xml`) are included in `@yleisradio/yds-core` under `tokens/build/android/`.
## Infographics
```bash
npm install @yleisradio/yds-core @yleisradio/yds-infographic-components-react @yleisradio/yds-infographic-icons-react
```
`@yleisradio/yds-core` exports the infographic colour themes as `infographicLight`, `infographicDark` and `infographicDefault` (CSS custom properties).
## Contributing code
Local setup, commit conventions and the release flow are in the repository [README](https://github.com/Yleisradio/yle-design-system#development). Before opening a pull request for anything larger than a fix, see [Support and contributing](./support.md).
# Introduction
Yle Design System (YDS) is the shared foundation for Yle's digital services. Designers and developers get the same building blocks — design tokens, components, icons and guidelines — so services stay consistent, accessible and on-brand without every team solving the same problems again.
## Why YDS exists
- **A shared language.** Designers and developers use the same names for the same things, in Figma and in code.
- **Build once, use everywhere.** Common UI solutions are built and tested once and reused across services, so teams can focus on what is new.
- **Accessibility and brand built in.** Components and tokens carry Yle's visual identity and accessibility requirements, so services don't have to reinvent them.
## What YDS covers
- **Design tokens:** colours and themes, typography, spacing, border radius, borders, shadows and transitions.
- **Components:** accessible, generic UI building blocks such as buttons, form controls, notifications, dialogs and navigation, for React and React Native, with matching Figma components.
- **Icons and logos:** basic icons, illustrative icons, Yle logos and graphical assets such as age-rating badges.
- **Infographics:** colour themes, icons and chart components for data visualisation.
- **Guidelines:** usage, content and accessibility guidance for each component, plus design principles such as motion.
## What YDS doesn't cover
- **Service-specific components and layouts.** YDS components are deliberately generic building blocks. Services compose them into their own views, and components that only one service needs stay in that service.
- **Native iOS and Android components.** Native apps can use YDS icons and colour tokens, but YDS doesn't provide native UI components.
- **Brand guidelines beyond digital UI.** Marketing and other non-digital touchpoints follow Yle's brand guidelines.
## Who it's for
- **Designers** work with the Figma libraries or prototype with AI agents. See [For designers](./for-designers.md).
- **Developers** use the npm packages. See [For developers](./for-developers.md).
- **Everyone building Yle's services**, product owners and editors included, can use these docs to see what is available and how it is meant to be used.
- **AI agents** read the documentation as plain Markdown and install the same packages. See [For AI agents](./for-ai-agents.md).
## Accessibility
YDS aims to meet [WCAG 2.2](https://www.w3.org/TR/WCAG22/) level AA in every component and token pairing. Components handle keyboard interaction, focus, semantics and colour contrast. Services remain responsible for their content, page structure and how components are combined. Each component page has an Accessibility section that lists what the component provides.
## How YDS is maintained
Yle's design system team maintains YDS. Designers and developers from Yle's services contribute to it, and new components start from needs in the services. See [Support and contributing](./support.md) for how to ask, report and propose.
## Next steps
- [Overview](./overview.md): what the system consists of and where to find each part.
- [For designers](./for-designers.md)
- [For developers](./for-developers.md)
- [For AI agents](./for-ai-agents.md)
- [Components](../components/index.md)
# Overview
YDS is made of a few parts that share the same tokens and names. Design tokens start as Figma variables and are built into code formats in `@yleisradio/yds-core`, so a colour or spacing value means the same thing in a design file and in an application.
| Part | Used for | Where |
| --- | --- | --- |
| Documentation | Guidelines, usage, API reference | This site |
| Figma libraries | Designing with components, tokens and icons | [YDS Foundation](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation) and others, see below |
| npm packages | Building with tokens, components, icons and fonts | Yle's GitHub Packages registry, see below |
| Storybook | Trying out components and their props | [Storybook](https://ds-storybook.test.yle.fi/) |
| CDN | Fonts and native icon files without installing packages | `design-system.cdn.yle.fi`, see below |
| Source code | Packages, docs and Storybook | [GitHub](https://github.com/Yleisradio/yle-design-system) |
## What each part contains
```mermaid
block-beta
columns 4
hFigma["Figma libraries"] hRepo["YDS repository"]:2 hDelivery["Delivery"]
foundation["YDS Foundation tokens, themes, text styles, components"]
core["yds-core tokens and themes all"]
themeCustom["yds-theme-custom custom themes all"]
storybook["Storybook interactive component examples"]
globalColors["YDS Global Colors global palettes, colour swatches"]
icons["yds-icons UI icons allreactreact native"]
infographicIcons["yds-infographic-icons weather icons allreactreact native"]
npm["npm packages GitHub Packages registry"]
infographics["YDS Infographics colour themes, weather symbols, chart examples"]
componentsReact["yds-components UI components react"]
componentsNative["yds-components UI components react native"]
cdn["CDN fonts and icons for native apps"]
iconsLogos["YDS Icons and Logos icons, illustrative icons, logos, graphical assets"]
infographicComponents["yds-infographic- components charts react"]
fonts["yds-fonts Yle Next font files all"]
site["Docs site Markdown and llms.txt for agents"]
space
docs["Documentation guidelines, usage, API reference"]:2
space
classDef header fill:none,stroke:none,font-weight:bold
classDef prerelease stroke-dasharray:5 4
class hFigma,hRepo,hDelivery header
class themeCustom,componentsNative,infographicComponents prerelease
```
- **Tags** show the platform packages: all works anywhere (CSS, JavaScript, SVG); react and react native are separate packages with those suffixes. For example, `yds-components` react is `@yleisradio/yds-components-react`.
- **Dotted boxes** are 0.x packages: usable, but their APIs may still change between minor versions.
- **In the diagram below**, `yds-core` stands for the token and theme packages, `yds-icons` for the icon packages and `yds-components` for the component packages.
## How the parts connect
```mermaid
%%{init: {"flowchart": {"nodeSpacing": 14, "rankSpacing": 40}}}%%
flowchart LR
hFigma[Figma libraries]:::header ~~~ hRepo[YDS repository]:::header ~~~ hDelivery[Delivery]:::header ~~~ hUse[Using YDS]:::header
foundation[YDS Foundation]
globalColors[YDS Global Colors]
infographics[YDS Infographics]
iconsLogos[YDS Icons and Logos]
components[yds-components]
tokens[yds-core]
icons[yds-icons]
docsSource[Documentation]
storybook[Storybook]
npm[npm packages]
cdn[CDN]
site[Docs site and llms.txt]
services[Yle services]
prototypes[AI prototypes]
foundation --> components
foundation & globalColors & infographics --> tokens
infographics & iconsLogos --> icons
iconsLogos ~~~ docsSource
components --> storybook & npm
tokens & icons --> npm & cdn
docsSource --> site
npm --> services & prototypes
cdn --> services
site -.-> services & prototypes
classDef header fill:none,stroke:none,font-weight:bold
```
- **Tokens** are defined as Figma variables and built into CSS, JavaScript, SCSS, Less and Android formats in `@yleisradio/yds-core`.
- **Icons** are drawn in Figma, exported as SVG and generated into the icon packages for web, React and React Native.
- **Components** are built on the tokens and icons, shown in Storybook and released as npm packages together with the token and icon packages.
- **Services and prototypes** install the packages and follow the documentation. What they learn comes back as feedback and contributions to the Figma libraries and the code.
## Figma libraries
| Library | Contents |
| --- | --- |
| [YDS Foundation](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation) | Design tokens as Figma variables, light and dark themes, text styles and shared components |
| [YDS Global Colors](https://www.figma.com/design/3ARTAZ2XrJHPq2g8iSBORW/YDS-Global-Colors) | Global palettes and colour swatches |
| [YDS Icons and Logos](https://www.figma.com/design/lSboxfILj3Thr1o145POJv/YDS-Icons-and-Logos) | Basic icons, illustrative icons, logos and graphical assets |
| [YDS Infographics](https://www.figma.com/design/ttv0a97s8ViuxBHtRyn3n2/YDS-Infographics) | Infographic colour themes, weather symbols and examples of simple infographic components |
How to enable them: [Using YDS in Figma](./for-designers.md#using-yds-in-figma).
## Packages
All packages are published under the `@yleisradio` scope to Yle's GitHub Packages registry. Installing them requires access: see [For developers](./for-developers.md#get-access-to-the-packages).
| Package | Contents | Platform |
| --- | --- | --- |
| `@yleisradio/yds-core` | Design tokens and themes as CSS custom properties, JS/TS constants, SCSS, Less and Android colour resources | All |
| `@yleisradio/yds-fonts` | Yle Next font files | All |
| `@yleisradio/yds-components-react` | React components | Web |
| `@yleisradio/yds-components-react-native` | React Native components | Mobile |
| `@yleisradio/yds-icons` | Icon SVG files and icon name types | All |
| `@yleisradio/yds-icons-react` | Icons as React components | Web |
| `@yleisradio/yds-icons-react-native` | Icons as React Native components | Mobile |
| `@yleisradio/yds-theme-custom` | Generating accessible custom themes from a colour combination | Web |
| `@yleisradio/yds-infographic-components-react` | Column, line, area and donut charts, legends and accessible data tables | Web |
| `@yleisradio/yds-infographic-icons` | Infographic icon SVG files, such as weather symbols | All |
| `@yleisradio/yds-infographic-icons-react` | Infographic icons as React components | Web |
| `@yleisradio/yds-infographic-icons-react-native` | Infographic icons as React Native components | Mobile |
Release notes for every package are in [GitHub releases](https://github.com/Yleisradio/yle-design-system/releases).
## Storybook
[Storybook](https://ds-storybook.test.yle.fi/) shows every React component in isolation, with controls for trying out props and states. Component pages on this site link to the matching story.
React Native components have their own Storybook in the [YDS repository](https://github.com/Yleisradio/yle-design-system). It isn't published on the web: run it locally on an iOS or Android simulator with `yarn storybook:native`. See the repository README for setup.
## CDN
`design-system.cdn.yle.fi` serves static files that don't need a package install:
- **Fonts:** [`fonts.css`](https://design-system.cdn.yle.fi/fonts/fonts.css) loads Yle Next with `@font-face` rules. See [Load Yle Next](../using-yds/development/get-started-with-v3.md#2-load-yle-next).
- **iOS icons (SVG):** [basic icons](https://design-system.cdn.yle.fi/icons/ios/icons.tar.gz), [illustrative icons](https://design-system.cdn.yle.fi/icons/ios/illustrative-icons.tar.gz)
- **Android icons (VectorDrawable):** [basic icons](https://design-system.cdn.yle.fi/icons/android/icons.tar.gz), [illustrative icons](https://design-system.cdn.yle.fi/icons/android/illustrative-icons.tar.gz)
# Support and contributing
| I want to… | Where |
| --- | --- |
| Ask a question or get help | [#design-system](https://yle.slack.com/archives/C01KDJ7JX5Y) on Slack |
| Discuss about technical issue | [#ds-kehitys](https://yle.slack.com/archives/C02G2TP79HB) on Slack |
| Report a bug | [New issue](https://github.com/Yleisradio/yle-design-system/issues/new/choose) → **Bug report** |
| Suggest a change to an existing component, token or icon | [New issue](https://github.com/Yleisradio/yle-design-system/issues/new/choose) → **Change request** |
| Propose a new component or feature | [New issue](https://github.com/Yleisradio/yle-design-system/issues/new/choose) → **New feature** |
| Explore an early design idea | [New issue](https://github.com/Yleisradio/yle-design-system/issues/new/choose) → **Concept design** |
| See what is being worked on | [Project board](https://github.com/orgs/Yleisradio/projects/32) |
| See what has changed | [GitHub releases](https://github.com/Yleisradio/yle-design-system/releases) |
| Fix or improve a docs page | **Edit this page** link at the top of the page |
Release announcements are also posted on #design-system.
## Before you ask
Search these docs, [Storybook](https://ds-storybook.test.yle.fi/) and [existing issues](https://github.com/Yleisradio/yle-design-system/issues?q=is%3Aissue), open and closed. Someone may have asked already.
## Reporting a bug
A good bug report includes:
- the package and its version
- where it happens: browser, device, React or React Native version
- steps to reproduce, with a code sample or a link if possible
- what you expected and what happened instead.
Don't put security-sensitive details in an issue. Contact the team on Slack and they'll point you to a private channel.
## Proposing a change or a new component
Open an issue before you start designing or building. The team checks that the proposal fits the system:
- Does more than one service need it, or could it be generalised so that they would?
- Could an existing component be extended instead?
- Is it accessible and built on existing design tokens?
- Does it work in both light and dark themes?
Once the proposal is agreed, the design is done in the YDS Figma libraries and the implementation as a pull request.
## Contributing code
Pull requests are welcome. Before opening one:
- Set up the repository with the [README](https://github.com/Yleisradio/yle-design-system#development) instructions.
- Write commit messages as [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
- Add a [changeset](https://github.com/Yleisradio/yle-design-system/blob/main/.changeset/README.md) for every change that should release a new package version.
- Document new components with the [docs page template](https://github.com/Yleisradio/yle-design-system/blob/main/docs/template.mdx).
More detail: [CONTRIBUTING.md](https://github.com/Yleisradio/yle-design-system/blob/main/CONTRIBUTING.md).
---
# Components
# Accordion
An Accordion lets users hide and reveal sections of related content. It helps reduce visual clutter by showing only section titles by default, while allowing users to expand the content they need.
- [Figma](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation?m=auto&node-id=285-1938&t=lI9imRC4H4TmzLE4-1)
- [Storybook](https://ds-storybook.test.yle.fi/?path=/docs/components-accordion--docs)
- [Edit this page](https://github.com/Yleisradio/yle-design-system/blob/main/docs/components/Accordion/Accordion.mdx)
- [Create a new issue](https://github.com/Yleisradio/yle-design-system/issues/new)
*[Live preview](https://design-system.yle.fi/docs/components/Accordion/)*
Code example
```tsx
import { Accordion, AccordionItem } from '@yleisradio/yds-components-react';
Content for first item
Content for second item
```
## Why to use
Accordion helps users navigate large amounts of content by grouping related information into collapsible sections. It reduces visual clutter and cognitive load by showing only section titles by default, while still allowing users to access detailed content when needed. Accordions are especially useful when most users need only part of the available information, such as in help content, rules, or frequently asked questions.
## When to use
Use an Accordion when content can be divided into independent, optional sections and it is not essential for all content to be visible at once. Accordions work well when users benefit from scanning headings first and choosing what to expand.
> **Do**
> - Use an Accordion for supplementary or secondary content.
> - Use when sections can be read independently, in any order.
> - Use for FAQs, instructions, rules, help content, and settings.
> - Use when reducing vertical scrolling improves clarity and scannability.
> **Don't**
> - Don’t hide critical or mandatory information inside an Accordion.
> - Don’t use Accordion for navigation between views — use [NavigationTabs](../NavigationTabs/NavigationTabs.md) or [SectionTabs](../SectionTabs/SectionTabs.md) instead.
> - Don’t use Accordion when content requires focused attention or user action — use a [Dialog](../Dialog/Dialog.md) ([Modal](../Modal/Modal.md) or [BottomSheet](../BottomSheet/BottomSheet.md)) instead.
> - Don’t use Accordion when there is only one short expandable section — use read more buttton/show more button instead.
> - Don’t nest Accordions inside other Accordions.
## Content Guidelines
Accordion titles are the primary decision point for users. Users decide whether to expand a section based on the title alone, so titles must clearly describe what the content contains.
> **Do**
> - Use short, descriptive titles that summarize the content (e.g. “Säännöt ja pistejako”, “Palkinnot”, “Mikä Tietäjä on?”).
> - Phrase titles in a way that matches the content type — questions work well for FAQs.
> - Keep wording parallel across items to support scanning.
> - Place the most commonly needed or most important item first.
> **Don't**
> - Don’t use vague titles like “Lisätietoja” or “Muut”.
> - Don’t use long sentences or explanatory text as titles.
> - Don’t repeat the same wording across multiple items.
> - Don’t rely on the expanded content to explain what the section is about.
## Anatomy

1. **Icon** – Visual indicator that shows whether the section is expanded or collapsed.
2. **Title** – The clickable header that summarizes the content of the section.
3. **Group container** – Wraps the title and content and manages spacing, alignment, borders, and wrapping.
4. **Closed item** – The closed state of the accordion item.
5. **Open item** – The open state of the accordion item.
6. **Content** – The section that contains the detailed content, revealed when the item is expanded.
7. **Border** – Optional visual separator between sections.
## Key Accordion Props
Use these props to configure the Accordion component.
### children
The AccordionItem components to display.
| Type | Example | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| `React.ReactNode` |
Content 1Content 2
| Child AccordionItem components |
Code example
```tsx
Content 1Content 2
```
## Key AccordionItem Props
Use these props to configure the AccordionItem component.
### title
The title text or content displayed in the accordion header.
| Type | Example | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `React.ReactNode` |
Content
| Title content for the accordion item |
Code example
```tsx
ContentBold Title}>Content
```
### variant
Visual style variant of the accordion item.
| Value | Example | Description |
| ---------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| `default` |
Content
| Default style with border |
| `noBorder` |
Content
| Style without border or padding |
Code example
```tsx
Content
Content
```
### size
Size of the accordion item.
| Value | Example | Description |
| ----- | ---------------------------------------------------------------------------------------------------------------- | ------------------- |
| `sm` |
Content
| Small size |
| `md` |
Content
| Default medium size |
| `lg` |
Content
| Large size |
Code example
```tsx
Content
Content
Content
```
### defaultOpen
Whether the accordion item should be open by default.
| Type | Example | Description |
| --------- | ------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| `boolean` |
Content
| Opens the item on initial render |
Code example
```tsx
Content
Content
```
### isDisabled
Whether the accordion item is disabled.
| Type | Example | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| `boolean` |
Content
| Disables interaction with the accordion item |
Code example
```tsx
Content
```
### removePadding
Removes padding from the accordion content.
| Type | Example | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `boolean` |
Content
| Removes padding from content area |
Code example
```tsx
Content
```
### contentProps
Additional props for the content container.
| Type | Example | Description |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| `{ padding?: 'default' \| 'horizontal' \| 'vertical' \| 'none' }` |
ContentContentContent 3Content 4
| Custom padding configuration for content |
Code example
```tsx
Content
Content
Content
Content
```
## Behavior
- Each accordion item expands and collapses independently.
- Multiple items can be open at the same time.
- Clicking the title toggles the expanded state.
- The icon (chevron) rotates or switches to reflect expanded vs collapsed state.
## Accessibility
- Provide a unique id on each AccordionItem for proper labeling and control linkage.
- Accordion headers must be focusable and keyboard-activatable so users can expand and collapse items with the keyboard (for example, using Enter or Space).
([WCAG 2.1.1 — Keyboard](https://www.w3.org/WAI/WCAG22/Understanding/keyboard))
- Ensure both the header and the panel have an accessible name by using clear, descriptive titles and avoiding empty panels.
([WCAG 2.4.6 — Headings and Labels](https://www.w3.org/WAI/WCAG22/Understanding/headings-and-labels))
## Implementation examples
### FAQ
Use an Accordion for frequently asked questions so users can expand only the answers they need.
*[Live preview](https://design-system.yle.fi/docs/components/Accordion/)*
Code example
```tsx
Tilauksen voit peruuttaa Asetukset-sivulta tai ottamalla yhteyttä asiakaspalveluun.
Toimitus tapahtuu 1–3 arkipäivän kuluessa tilauksesta.
Kyllä, maksutavan voit vaihtaa tilin asetuksista milloin tahansa.
```
### One item open by default
Use `defaultOpen` to show one section expanded on first load so users see sample content without clicking.
*[Live preview](https://design-system.yle.fi/docs/components/Accordion/)*
Code example
```tsx
Content for first item
Content for second item (open by default)
Content for third item
```
## API Reference
### Accordion Props
The Accordion component accepts all standard HTML `
` attributes in addition to the following props:
| Prop | Type | Required | Default | Description |
| ---------- | ----------------- | -------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode` | No | — | AccordionItem components to display |
### AccordionItem Props
The AccordionItem component accepts all standard HTML `` attributes in addition to the following props:
| Prop | Type | Required | Default | Description |
| --------------- | ----------------------------------------------------------------- | -------- | ----------- | -------------------------------------- |
| `title` | `React.ReactNode` | Yes | — | Title content for the accordion item |
| `id` | `string` | No | — | Unique identifier for accessibility |
| `variant` | `'default' \| 'noBorder'` | No | `'default'` | Visual style variant |
| `size` | `'sm' \| 'md' \| 'lg'` | No | `'md'` | Size of the accordion item |
| `defaultOpen` | `boolean` | No | `false` | Whether the item is open by default |
| `isDisabled` | `boolean` | No | `false` | Whether the item is disabled |
| `removePadding` | `boolean` | No | — | Removes padding from content area |
| `contentProps` | `{ padding?: 'default' \| 'horizontal' \| 'vertical' \| 'none' }` | No | `{}` | Additional props for content container |
| `children` | `React.ReactNode` | No | — | Content displayed when expanded |
### Type Definitions
```tsx
export type AccordionSize = 'sm' | 'md' | 'lg';
export type Padding = 'default' | 'horizontal' | 'vertical' | 'none';
export interface AccordionContentDSProps {
padding?: Padding;
}
export interface AccordionItemDSProps {
id?: string;
variant?: 'default' | 'noBorder';
title: React.ReactNode;
size?: AccordionSize;
defaultOpen?: boolean;
isDisabled?: boolean;
contentProps?: AccordionContentDSProps;
children?: React.ReactNode;
removePadding?: boolean;
}
export interface AccordionDSProps {
children?: React.ReactNode;
}
export type AccordionProps = HTMLAttributes & AccordionDSProps;
export type AccordionItemProps = HTMLAttributes & AccordionItemDSProps;
```
## Related Components
- [Button](../Button/Button.md) – Can be used inside accordion content
- [SectionTabs](../SectionTabs/SectionTabs.md) – Alternative component for organizing content
- [NavigationTabs](../NavigationTabs/NavigationTabs.md) – Alternative component for navigation between views
- [Dialog](../Dialog/Dialog.md) – Alternative component for showing content in a modal
- [BottomSheet](../BottomSheet/BottomSheet.md) – Alternative component for showing content in a bottom sheet
import {
Link2,
Mail,
Whatsapp,
Facebook,
TwitterX,
Linkedin,
Share,
} from '@yleisradio/yds-icons-react';
import { Resources } from 'yds-documentation/src/components/Resources/Resources';
import { ComponentPreview } from 'yds-documentation/src/components/ComponentPreview/ComponentPreview';
# ActionMenu
ActionMenu is a button that opens a dropdown list of actions. It groups multiple related actions into a compact control without changing the component’s state when an item is selected.
- [Figma](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation?node-id=8809-10634&p=f&t=U4uSG0M5o85iz0VX-0)
- [Storybook](https://ds-storybook.test.yle.fi/?path=/docs/components-actionmenu--docs)
- [Edit this page](https://github.com/Yleisradio/yle-design-system/blob/main/docs/components/ActionMenu/ActionMenu.mdx)
- [Create a new issue](https://github.com/Yleisradio/yle-design-system/issues/new)
*[Live preview](https://design-system.yle.fi/docs/components/ActionMenu/)*
Code example
```tsx
import { ActionMenu, MenuItem, MenuSeparator } from '@yleisradio/yds-components-react';
```
## Why to use
ActionMenu provides a clear and space-efficient way to expose multiple related actions from a single entry point. Instead of showing several buttons side by side, ActionMenu keeps the interface uncluttered while still making all actions easily accessible.
It is built on top of Menu, ensuring consistent positioning, keyboard navigation, and accessibility across products while supporting typical action-based use cases such as sharing, editing, or managing content.
## When to use
Use ActionMenu when the user needs to trigger one of several related actions that do not affect the component’s state.
> **Do**
> - Use when multiple related actions are available from the same context.
> - Use for secondary or contextual actions such as sharing, editing, or managing content.
> - Use when space is limited and showing all actions at once would clutter the UI.
> **Don't**
> - Don’t use for selecting values or changing component state (use [DropdownMenu](../DropdownMenu/DropdownMenu.md) instead).
> - Don’t use for navigation between views or pages.
> - Don’t use for a single action (use a [Button](../Button/Button.md) instead).
## Content Guidelines
ActionMenu items represent actions, not values.
> **Do**
> - Use verbs for menu items (e.g. “Jaa”, “Muokkaa”, “Poista”).
> - Keep labels concise and action-oriented.
> - Group related actions using separators when necessary.
> **Don't**
> - Don’t use nouns or descriptive values as menu items.
> - Don’t use vague labels like “OK” or “Kyllä”.
> - Don’t mix actions with selection-type options in the same menu.
## Anatomy

1. **ActionMenu button** – The trigger that opens the list of actions.
2. **Label** – Text describing the category of actions (e.g. “Actions”, “Share”).
3. **Chevron icon** – Indicates that the button opens a dropdown.
4. **Menu surface** – The floating container holding the action items.
5. **MenuItem (action)** – A single actionable option inside the menu.
6. **MenuItem icon (optional)** – An optional leading icon inside a `MenuItem`
7. **MenuSeparator (optional)** – Groups related actions.
## Key ActionMenu Props
Use these props to configure the ActionMenu component.
### text
The text displayed on the action menu button.
| Type | Example | Description |
| -------- | ------------------------------------------------------------------------------------------------------------------- | ----------------- |
| `string` | | Button label text |
Code example
```tsx
```
### children
Menu items to display in the dropdown menu.
| Type | Example | Description |
| ----------------- | --------------------------------------------------------------------------------------------- | ------------------------------ |
| `React.ReactNode` | | MenuItem components to display |
Code example
```tsx
```
### dropdown
Whether to show a dropdown chevron icon.
| Value | Example | Description |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- |
| `true` | | Shows the chevron icon |
| `false` | | Hides the chevron icon |
Code example
```tsx
```
### openMenuOnClick
Whether clicking the button opens the menu.
| Type | Example | Description |
| --------- | --------------------------------------------------------------------------------- | -------------------------------- |
| `boolean` | | Controls if click opens the menu |
Code example
```tsx
```
### menuSize
Size of the menu items.
| Value | Example | Description |
| ----- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `sm` | | Small menu items |
| `md` | | Default medium menu items |
Code example
```tsx
```
### alignMenu
Horizontal alignment of the menu relative to the button.
| Value | Example | Description |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| `left` | | Aligns menu to the left |
| `right` | | Aligns menu to the right (default) |
Code example
```tsx
```
### position
Preferred direction in which the menu opens relative to the trigger button. The menu automatically adjusts if it would be clipped by the viewport.
| Value | Example | Description |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| `bottom` | | Opens below the button (default) |
| `top` | | Opens above the button |
| `left` | | Opens to the left of the button |
| `right` | | Opens to the right of the button |
Code example
```tsx
```
## Behavior
- Selecting an item triggers the associated action immediately.
- The ActionMenu label does not change based on selection.
- The menu opens from the trigger and closes after selection, outside click, or cancellation.
- The `position` prop controls the preferred direction in which the menu opens: `'bottom'` (default), `'top'`, `'left'`, or `'right'`. The menu automatically adjusts if it would be clipped by the viewport.
- Keyboard interaction:
- Arrow keys navigate between actions.
- Enter activates the focused action.
- Escape closes the menu.
## Accessibility
- Ensure full keyboard support for all interactions.
(WCAG 2.1.1 — [Keyboard](https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html))
- Menu items have clear visual focus indicators.
([WCAG 2.4.7 — Focus Visible](https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html))
- Each menu item exposes a clear, descriptive action name to assistive technologies.
(WCAG 4.1.2 — [Name, Role, Value](https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html))
- The menu state (open/closed) is communicated programmatically.
- Actions are perceivable and understandable without relying on icons alone.
## Implementation Examples
### Share menu
Social sharing links. There's a package for [Share Button](https://github.com/Yleisradio/share-button) available for easier sharing functionality.
*[Live preview](https://design-system.yle.fi/docs/components/ActionMenu/)*
Code example
```tsx
import { ActionMenu, MenuItem } from '@yleisradio/yds-components-react';
import { Link2, Mail, Whatsapp, Facebook, TwitterX, Linkedin } from '@yleisradio/yds-icons-react';
} onSelect={() => navigator.clipboard.writeText('https://example.com')}>Kopioi linkki
} onSelect={() => window.open('mailto:?subject=Title&body=https://example.com')}>Sähköposti
} onSelect={() => window.open('https://wa.me/?text=https://example.com', '_blank')}>WhatsApp
} onSelect={() => window.open('https://www.facebook.com/sharer/sharer.php?u=https://example.com', '_blank')}>Facebook
} onSelect={() => window.open('https://x.com/intent/post?url=https://example.com', '_blank')}>X-viestipalvelu
} onSelect={() => window.open('https://www.linkedin.com/sharing/share-offsite/?url=https://example.com', '_blank')}>LinkedIn
```
## API Reference
### Props
The ActionMenu component accepts all standard Button props (except `as` and `onClick`) in addition to the following props:
| Prop | Type | Required | Default | Description |
| ----------------- | ------------------- | -------- | --------- | ------------------------------------- |
| `text` | `string` | Yes | — | Button label text |
| `id` | `string` | Yes | — | Unique identifier for the button |
| `menuId` | `string` | Yes | — | Unique identifier for the menu |
| `children` | `React.ReactNode` | Yes | — | MenuItem components to display |
| `dropdown` | `boolean` | No | `true` | Whether to show dropdown chevron icon |
| `openMenuOnClick` | `boolean` | No | `true` | Whether clicking opens the menu |
| `onClose` | `() => void` | No | — | Callback fired when menu closes |
| `onOpen` | `() => void` | No | — | Callback fired when menu opens |
| `menuSize` | `'sm' \| 'md'` | No | `'md'` | Size of menu items |
| `alignMenu` | `'left' \| 'right'` | No | `'right'` | Horizontal alignment of menu |
| `position` | `'top' \| 'right' \| 'bottom' \| 'left'` | No | `'bottom'` | Preferred direction the menu opens |
### Type Definitions
```tsx
export interface ActionMenuDSProps {
text: string;
id: string;
menuId: string;
children: ReactNode;
dropdown?: boolean;
openMenuOnClick?: boolean;
onClose?: () => void;
onOpen?: () => void;
menuSize?: 'md' | 'sm';
alignMenu?: 'left' | 'right';
position?: 'top' | 'right' | 'bottom' | 'left';
}
export type ActionMenuProps = ActionMenuDSProps & Omit, 'as' | 'onClick'>;
```
## Related Components
- [DropdownMenu](../DropdownMenu/DropdownMenu.md) – Alternative component for selectable menu options
- [Button](../Button/Button.md) – ActionMenu extends Button functionality
- [Menu](../Menu/Menu.md) - Base component for ActionMenu
# Badge
Badges are used to indicate a notification, item count, or other contextual information related to a navigation destination.
- [Figma](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation?node-id=4240-129)
- [Storybook](https://ds-storybook.test.yle.fi/?path=/docs/components-badge--docs)
- [Edit this page](https://github.com/Yleisradio/yle-design-system/blob/main/docs/components/Badge/Badge.mdx)
- [Create a new issue](https://github.com/Yleisradio/yle-design-system/issues/new)
*[Live preview](https://design-system.yle.fi/docs/components/Badge/)*
Code example
```tsx
import { Badge } from '@yleisradio/yds-components-react';
3
```
## Why to Use
The Badge component visually indicates a status, count, or notification associated with another UI element. It helps users quickly identify new information, pending actions, or system alerts without needing to open the full view. They are typically displayed inside or attached to another component, such as an icon, button, or tab.
Badges are commonly used in navigation or profile contexts — for example, to show the number of unread messages, notifications, or active tasks. They are non-interactive and rely on their placement and appearance to convey meaning clearly.
## When to Use
Use Badge when you want to draw attention to updates, counts, or statuses related to another UI element.
## Content guidelines
> **Do**
> - Attach the badge directly to a relevant component (e.g. icon, button, tab).
> - Use it to display numeric counts or simple state indicators.
> - Choose a size that keeps the content legible relative to the parent.
> **Don’t**
> - Don’t use Badge as an interactive control.
## Key Props
Use the following props to customize the Badge component to fit your needs.
### size
Controls the visual dimensions and typography. Smaller sizes (`sm`, `md`) are suited to minimal indicators (dots); larger sizes (`lg`, `xl`) accommodate numeric content.
| Value | Example | Description |
|------|---------|------------------|
| `sm` | | Tiny status indicator without text. Used in upper right corner of navigation item. |
| `md` | | Small status/count indicator without text. Used in upper right corner of navigation item. |
| `lg` | 3 | Default size. |
| `xl` | 12 | Prominent counts or emphasis. For inline use. |
Code example
```tsx
312
```
### color
Defines semantic color scheme.
| Value | Example | Description |
|-------|---------|---------|
| `default` | 4 | Standard action background; generic counts. |
| `negative` | 2 | Neutral/subtle emphasis; lower visual weight. |
| `highlight` | 12 | High-attention feedback. Use sparingly. |
Code example
```tsx
4212
```
### variant
Alters structural presentation (border & padding). `primary` is a filled pill; `secondary` applies an outline for large sizes (`lg`, `xl`).
| Value | Example | Description |
|---------|---------|-------------|
| `primary` | 12 | Filled default style. |
| `secondary` | 12 | Outline improves contrast against background but reduces badge size |
Code example
```tsx
331212
```
## Accessibility
- Use `aria-label` when numeric value or color alone lacks context (e.g. `7`).
- Keep numbers short; truncate or summarize large counts (e.g. `99+`).
- Use ARIA attributes (e.g., aria-label, aria-live) to make changes accessible.
- Avoid animations or flashing that may cause distraction or accessibility issues.
Code example
```tsx
7
```
[See more examples in Storybook](https://ds-storybook.test.yle.fi/?path=/docs/components-badge--docs)
## API Reference
### Props
The Badge component accepts all standard HTML `` attributes in addition to the following props:
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `size` | `BadgeSizeProp` | No | `'lg'` | Size of the Badge |
| `children` | `React.ReactNode` | No | — | |
| `color` | `BadgeColorProp` | No | `'default'` | Color of the Badge |
| `variant` | `BadgeVariantProp` | No | `'primary'` | Visual presentation of the Badge |
### Type Definitions
```tsx
export type BadgeSizeProp = 'xl' | 'lg' | 'md' | 'sm';
export type BadgeColorProp = 'default' | 'negative' | 'highlight';
export type BadgeVariantProp = 'primary' | 'secondary';
export interface BadgeDSProps {
size?: BadgeSizeProp;
children?: React.ReactNode;
color?: BadgeColorProp;
variant?: BadgeVariantProp;
}
export type BadgeProps = HTMLAttributes & BadgeDSProps;
```
## Related Components
- [Button](../Button/Button.md) – Badges often attach to buttons to show counts.
- [NavigationTabs](../NavigationTabs/NavigationTabs.md) – Use Badges to show per-tab notification or item counts.
- [Tag](../Tag/Tag.md) – Static keyword label without counts.
- [TagLink](../TagLink/TagLink.md) – Interactive keyword navigation.
- [TagFilter](../TagFilter/TagFilter.md) – Removable filter chips.
# BannerNotification
A BannerNotification displays important temporary messages that affect the entire view or service, appearing below the header and remaining visible until dismissed.
- [Figma](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation?m=auto&node-id=7323-2007&t=lI9imRC4H4TmzLE4-1)
- [Storybook](https://ds-storybook.test.yle.fi/?path=/docs/components-bannernotification--docs)
- [Edit this page](https://github.com/Yleisradio/yle-design-system/blob/main/docs/components/BannerNotification/BannerNotification.mdx)
- [Create a new issue](https://github.com/Yleisradio/yle-design-system/issues/new)
The service will be unavailable on Sunday from 02:00 to 04:00.
Code example
```tsx
import { BannerNotification } from '@yleisradio/yds-components-react';
The service will be unavailable on Sunday from 02:00 to 04:00.
```
## Why to use
BannerNotifications communicate important service- or view-wide information that users must notice. They are designed for messages that affect the entire experience, such as maintenance, outages, or access limitations, and therefore require high visibility and persistence.
Unlike Inline Notifications, which relate to specific content, or Toasts, which are brief and transient, BannerNotifications provide sustained awareness across the whole view.
## When to use
BannerNotifications are used to communicate important information that affects the entire page or service and must be noticed by all users.
> **Do**
> - Use a BannerNotification for service-wide or view-wide messages.
> - Use it for system states such as maintenance, outages, access restrictions, or critical warnings.
> - Use it when the message must remain visible until acknowledged or resolved.
> - Use it when users may need to take action outside the current context.
> **Don’t**
> - Don’t use BannerNotifications for local or field-level issues — use Inline [Notification](../Notification/Notification.md).
> - Don’t use them for short-lived confirmations or feedback — use a [Toast](../ToastNotification/ToastNotification.md).
> - Don’t show multiple banners at the same time.
> - Don’t make critical system messages dismissible without another way to access the information.
### Differences between Notification Types
| Type | Purpose | Placement | Use case |
| ----------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------- |
| **Banner Notification** | A notification covering the entire view or service (e.g., maintenance, error state). | Fixed, below the header. | “Maintenance in progress” |
| **Inline Notification** | A local notification within content or a form (e.g., field error). | Embedded in content. | “This field is required” |
| **Toast Notification** | A short-lived, automatically disappearing feedback message (e.g., save confirmation). | Floating near the bottom of the screen, sliding in and out smoothly. | “Saved successfully” |
## Content Guidelines
BannerNotification content should be clear, scannable, and focused on impact. Users should immediately understand what is happening and why it matters.
> **Do**
> - Use a clear, descriptive title (e.g., “Huoltokatko”).
> - Start with what is happening and why it matters (e.g., “Palvelu ei ole käytettävissä sunnuntaina 02.00–04.00”).
> - Keep body copy short (one–two sentences).
> - Add next steps when relevant (e.g., “Yritä uudelleen”, “Näytä lisätiedot”).
> - Use actions only when the user can meaningfully act.
> **Don’t**
> - Don’t write long or dense text.
> - Don’t repeat title text in the body.
> - Don’t use vague phrasing (e.g., “Tapahtui virhe”).
> - Don’t use jargon or filler.
## Anatomy

1. **Icon** – Icon indicating the message level (info, success, warning, error).
2. **Title** – Short heading.
3. **Body** – Main message content.
4. **Secondary Button (Actions)** – Optional supporting or alternative action.
5. **Primary Button (Actions)** – Main action guiding the user’s next step.
6. **Close Button** – Optional dismiss control that allows the user to close non-critical or informational notifications.
## Key BannerNotification Props
Use these props to configure the BannerNotification component.
### level
Sets semantic and visual level (icon + colors).
| Value | Example | Description |
| --------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `info` |
General information.
| Neutral informational context. |
| `success` | Task completed. | Positive confirmation. |
| `warning` | Check details. | Caution, requires attention soon. |
| `error` | Action failed. | Critical or blocking issue. |
Code example
```tsx
General information.Task completed.Check details.Action failed.
```
### title
Optional short heading preceding body.
| Type | Example | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------- | --------------------------- |
| `ReactNode` |
Service updated.
| Concise summary of message. |
Code example
```tsx
Service updated.
```
### children (body)
Main descriptive text.
| Type | Example | Description |
| ----------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| `ReactNode` |
Will occur Sunday.
| Explains status or guidance. |
Code example
```tsx
Will occur Sunday.Standalone message without title.
```
### hideIcon
Suppresses level icon (text/color still convey status).
| Type | Example | Description |
| --------- | ------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `boolean` |
Plain text only.
| Removes icon for minimal layout. |
Code example
```tsx
Plain text only.
```
### onClose (close button)
| Type | Example | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `() => void` |
{}} title="Dismissable">Close me.
| If provided, displays close button at the right side. Closing the notification needs to be handled in the callback. |
Code example
```tsx const [show, setShow] = useState(true);
{show && (
setShow(false)} title="Dismissable">
Close me.
)}
```
### actions
Inline actionable elements (e.g., Retry, View details).
| Type | Example | Description |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| `ReactNode` |
} title="Beta test">We are looking for user to participate in our beta test.
| Buttons aligned within banner. |
Code example
```tsx
}
title="Beta test"
>
We are looking for users to participate in our beta test.
```
### contentContainer
Custom wrapper component to constrain or style inner content.
| Type | Example | Description |
| ---------------------------------- | --------------------------------------------------------- | ----------------------------------- |
| `ComponentType` | [See implementation examples](#constrained-content-width) | Limits width / adds layout styling. |
## Behavior
- BannerNotifications appear directly below the global header and span the full width of the viewport.
- They remain visible until dismissed by the user or until the underlying condition is resolved.
- Only one BannerNotification should be shown per view at a time.
- BannerNotifications should not overlap or obscure primary page content.
- When actions are present, they are clearly labeled and consistently aligned.
- Dismissal is optional and should be avoided for critical system messages.
## Accessibility
- Place logically after the header in DOM order for easy navigation.
- Ensure close button includes a descriptive aria-label (e.g., “Close notification”).
- Ensure full keyboard accessibility and visible focus states for actions and controls.
- Critical information should remain available until resolved or acknowledged.
## Implementation examples
### With actions
}
>
Ohjelma katsottavissa EU:n alueella.Code example
```tsx
}
>
Ohjelma katsottavissa EU:n alueella.
```
### Constrained content width
*[Live preview](https://design-system.yle.fi/docs/components/BannerNotification/)*
## API Reference
### Props
The BannerNotification component accepts all standard HTML `
` attributes in addition to the following props:
| Prop | Type | Required | Default | Description |
| ------------------ | ---------------------------------- | -------- | -------- | ----------- |
| `children` | `React.ReactNode` | No | — | |
| `title` | `React.ReactNode` | No | — | |
| `level` | `NotificationLevel` | No | `'info'` | |
| `hideIcon` | `boolean` | No | — | |
| `onClose` | `() => void` | No | — | |
| `actions` | `React.ReactNode` | No | — | |
| `contentContainer` | `ComponentType` | No | — | |
### Type Definitions
```tsx
export type NotificationLevel = 'info' | 'success' | 'warning' | 'error';
export interface BannerNotificationDSProps {
children?: React.ReactNode;
title?: React.ReactNode;
level?: NotificationLevel;
hideIcon?: boolean;
onClose?: () => void;
actions?: React.ReactNode;
contentContainer?: ComponentType;
}
export type BannerNotificationProps = Omit<
HTMLAttributes,
keyof BannerNotificationDSProps
> &
BannerNotificationDSProps;
```
## Related Components
- [Notification](../Notification/Notification.md) – Inline contextual messages.
- [Toast Notification](../ToastNotification/ToastNotification.md) – Short-lived floating feedback.
# BottomSheet
BottomSheet displays content in a panel that slides up from the bottom of the viewport. It is optimized for mobile and touch-based interactions and can be either modal or non-modal. BottomSheet handles gestures, positioning, overlay behavior, and accessibility, and often contains a Dialog component for structured content.
- [Figma](https://www.figma.com/design/U1NzhzFpMpgaIDL7MbbrYE/YDS-Foundation?m=auto&node-id=9821-1268&t=lI9imRC4H4TmzLE4-1)
- [Storybook](https://ds-storybook.test.yle.fi/?path=/docs/components-bottomsheet--docs)
- [Edit this page](https://github.com/Yleisradio/yle-design-system/blob/main/docs/components/BottomSheet/BottomSheet.mdx)
- [Create a new issue](https://github.com/Yleisradio/yle-design-system/issues/new)
*[Live preview](https://design-system.yle.fi/docs/components/BottomSheet/)*
Code example
```tsx
import { BottomSheet, useBottomSheet, Button, Dialog } from '@yleisradio/yds-components-react';
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
<>
>
```
## Why to use
Use BottomSheet to present contextual content, actions, or settings while preserving the user’s sense of place. It provides a mobile-friendly alternative to Modal with natural touch interactions, flexible sizing, and optional non-modal behavior. BottomSheet standardizes mobile dialog behavior across YDS and ensures accessible gesture and keyboard handling.
## When to use
Use BottomSheet when content is closely related to the current view and especially in mobile or touch-first contexts.
> **Do**
> - Use BottomSheet for mobile dialogs, filters, and settings.
> - Use it when content should feel lightweight and dismissible.
> - Combine BottomSheet with [Dialog](../Dialog/Dialog.md) for structured content.
> **Don't**
> - Don’t use BottomSheet for critical confirmations that require full attention.
> - Don’t use BottomSheet for complex, multi-step workflows.
> - Don’t replace desktop modals with BottomSheet unnecessarily.
## Content Guidelines
Use these guidelines when placing content inside a BottomSheet.
> **Do**
> - Keep content task-focused and lightweight.
> - Prefer short forms and clear actions.
> - Use clear completion actions such as “Done” or “Apply”.
> **Don't**
> - Don’t overload the sheet with long scrolling content unless using the expanded size.
> - Don’t hide critical actions below excessive content.
> - Don’t rely on drag-to-close as the only way to dismiss the sheet.
## Anatomy

1. **Drag handle / close affordance** – Visual indicator that the sheet can be dragged or dismissed.
2. **Sheet container** – The surface that slides up from the bottom and contains all sheet content.
3. **Bottom Sheet title** – Required heading that describes the purpose of the sheet.
4. **Content area** – The main content section for text or interactive elements. Supports internal scrolling when content exceeds available height.
5. **Actions area (optional)** – Footer area for primary and secondary actions, aligned consistently across devices.
## Key BottomSheet Props
Use these props to configure the BottomSheet component.
### bottomSheetRef
Ref handle for controlling the bottom sheet (open/close). Use with `useBottomSheet` hook.
| Type | Example | Description |
| ------------------------------------ | --------------------- | ----------------------------------- |
| `React.RefObject` | | Ref handle for programmatic control |
Code example
```tsx
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
Content here
```
### title
Optional heading text displayed in the sheet header.
| Type | Example | Description |
| -------- | -------------------------------------- | ------------------------------- |
| `string` | | Title text for the bottom sheet |
Code example
```tsx
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
Content here
```
### titleAs
HTML tag name to render for the title.
| Type | Example | Description |
| -------- | ---------------------------------- | ----------------------------------- |
| `string` | | HTML element for title (e.g., 'h2') |
Code example
```tsx
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
Content here
```
### dragToClose
Enables pointer drag gesture and handle for closing the sheet.
| Value | Example | Description |
| ------- | ----------------------------------------- | ------------------------------------ |
| `true` | | Enables drag-to-close functionality |
| `false` | | Disables drag-to-close functionality |
Code example
```tsx
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
Content here
```
### align
Horizontal alignment of the sheet.
| Value | Example | Description |
| -------- | ------------------------------------ | ------------------------------ |
| `center` | | Centers the sheet horizontally |
| `left` | | Aligns sheet to the left |
| `right` | | Aligns sheet to the right |
Code example
```tsx
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
Content here
```
### nonModal
If true, renders a non-modal dialog (no backdrop, background remains interactive).
| Value | Example | Description |
| ------- | -------------------------------------- | ------------------------- |
| `true` | | Makes the sheet non-modal |
| `false` | | Makes the sheet modal |
Code example
```tsx
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
Content here
```
### closeOnOutsideClick
If true, clicking outside the sheet closes it (modal only).
| Value | Example | Description |
| ------- | ------------------------------------------------- | --------------------------------------- |
| `true` | | Controls outside click closing behavior |
| `false` | | Controls outside click closing behavior |
Code example
```tsx
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();
Content here
```
### size
Size variant of the bottom sheet.
| Value | Example | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| `compact` |
What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it? It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). Where does it come from? Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. Where can I get some? There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
| Compact size variant |
| `default` |
What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it? It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). Where does it come from? Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. Where can I get some? There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
| Default size variant |
| `expanded` |
What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it? It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). Where does it come from? Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. Where can I get some? There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
| Expanded size variant |
Code example
```tsx
```
## Behavior
- BottomSheet slides in from the bottom of the viewport and animates smoothly when opening and closing.
- The sheet opens when `openBottomSheet()` is called or when the ref’s `open()` method is invoked.
- The sheet closes when `closeBottomSheet()` is called, the close button or drag handle is activated, the Escape key is pressed, or the user drags the sheet down past the close threshold.
- Drag-to-close interaction is enabled when `dragToClose={true}`. If the drag distance exceeds the threshold (100px), the sheet closes; otherwise, it snaps back into place.
- Modal bottom sheets (default) block background interaction and disable body scrolling.
- Non-modal bottom sheets (`nonModal={true}`) allow background interaction and do not disable body scrolling.
- Focus and keyboard behavior adapt based on modality.
- The Escape key always closes modal bottom sheets and closes non-modal sheets only when focus is inside the sheet.
- Clicking outside the sheet closes it only when `closeOnOutsideClick={true}` and the sheet is modal.
- The `onClose` callback fires after the sheet has fully closed (native dialog `close` event).
- Size variants (`compact`, `default`, `expanded`) control how much vertical space the sheet initially occupies.
## Accessibility
- BottomSheet uses the native HTML `