Regular text, with comfortable line height for reading long paragraphs.
```
## Helper text
| Class | What it is |
| --- | --- |
| `.gm-muted` | Secondary text, in gray (7:1 or more, AAA) |
| `.gm-small` | Small text (0.84 rem) |
| `.gm-eyebrow` | Eyebrow: gray and semibold, above a heading |
```html
Secondary text for descriptions and hints.
Small text: dates, notes, legal notices.
```
## Data list
`.gm-dl`: label in gray and value below it, for summaries (the details of an order, a profile or a request).
```html
`: “Add the `.gm-btn` class to the button”.
```html
Add the .gm-btn class to the button.
```
## Block
`.gm-code` on a ``: gray background, rounded corners and a sideways scrollbar if a line is too long (text does not wrap).
```html
npm install glassmarshmallow
npm run build
```
If you prefer long lines to wrap instead of scrolling, add `style="white-space: pre-wrap"`.
## Accessibility
- Add `tabindex="0"` to blocks that can scroll sideways, so they can be moved with the keyboard (WCAG 2.1.1).
- If the block is long, name it with `aria-label` (“Install code”).
- Regular text on gray: 14:1 (AAA).
# Form rules
What a form needs to be easy to fill in and accessible. Every form built with GlassMarshmallow must follow these rules. They come from the GOV.UK guidance, the W3C forms tutorial and usability studies, and each one names the WCAG 2.2 criterion it helps meet.
## 1. Ask for as little as possible
Every extra field is a reason to give up. Before adding one, ask yourself whether you really need it now. If a piece of data only applies to some people, ask only them.
## 2. One column, label on top
Fields go one below the other, in the order people think of the answers. The label goes **above** the field, is short, starts with a capital letter and has no colon at the end. That way the form reads top to bottom with no jumps, also when zoomed in.
**Wrong:** two fields per row and labels beside them.
```html
First name:
Last name:
```
**Right:** one column and the label on top.
```html
```
WCAG: 1.3.2 Meaningful Sequence, 1.4.10 Reflow, 3.3.2 Labels or Instructions.
## 3. Simple questions
Several simple questions are better than one complicated one. Use closed questions (options to pick) when the possible answers are known, and allow “I don't know” when it is a valid answer. With 2 or 3 short options, in a row; with more, one below the other.
## 4. Mark required and optional fields
Required fields have “(required)” written in the label and `required` on the field; optional ones, “(optional)”. Written in words it needs no explanation and screen readers read it the same way; not everyone knows what an asterisk means.
```html
```
WCAG: 3.3.2 Labels or Instructions, 1.4.1 Use of Color (said in text, not with color).
## 5. Help text, visible and outside the field
Help goes as text below the label and is linked to the field with `aria-describedby`. The example text inside the field (`placeholder`) is not a label or help: it disappears when typing, usually has low contrast and many screen readers skip it.
**Wrong:** help only in the placeholder.
```html
ID number
```
**Right:** help as text, linked to the field.
```html
```
WCAG: 3.3.2 Labels or Instructions, 1.4.3 Contrast.
## 6. Clear error messages
The message says what happened and how to fix it, using the label's words. No blame (“you forgot…”), no technical words (“validation error”), no “please”, and not color alone: the field gets `aria-invalid="true"` and the message is linked with `aria-describedby`.
**Wrong:** does not say what to do.
```html
```
**Right:** says what is missing and gives an example.
```html
```
WCAG: 3.3.1 Error Identification, 3.3.3 Error Suggestion, 1.4.1 Use of Color.
## 7. Error summary on submit
If there are errors on submit, a summary appears above the form with how many there are and a link to each field, as well as the message next to each one. Focus moves to the summary so screen readers read it in full.
```html
There are 2 errors in the form
```
Each link goes to the field to fix (`href="#field-id"`) and repeats its error message. The summary title also goes in the page title: “Error: Contact | My site”.
WCAG: 3.3.1 Error Identification, 2.4.3 Focus Order, 4.1.3 Status Messages.
## 8. Validate without nagging
Check the data on submit or when leaving the field, not while typing: an error that shows up halfway through an email address only annoys. Remove the error as soon as the field is right. Never delete what the person typed.
WCAG: 3.3.1 Error Identification, 3.3.7 Redundant Entry (do not ask for the same thing twice).
## 9. Confirm and allow undo
On submit, say clearly that it worked, on the page and to screen readers (with `role="status"` or by moving focus to the message). If submitting has consequences (paying, deleting, signing), let people review the data first and undo or correct it afterwards.
```html
Message sent. I will reply within 2 working days.
```
WCAG: 4.1.3 Status Messages, 3.3.4 and 3.3.6 Error Prevention.
## 10. The button says what it does
The submit button says what happens when pressed: “Send request”, “Create account”, “Pay €25”. Avoid a bare “OK” or “Submit” when it is not clear what is sent. If there is a button to clear the form, do not put it next to the submit button with the same look.
```html
Send request
```
WCAG: 2.4.6 Headings and Labels, 2.5.3 Label in Name.
## Also, always
- Every field has its visible ``, and groups of options go in a `` with a ``.
- Use the right `type` and `autocomplete` (`email`, `tel`, `name`, `postal-code`…): phones show the right keyboard and the browser can fill in (WCAG 1.3.5).
- Everything can be filled in and sent with the keyboard alone, in order, and focus is always visible (WCAG 2.1.1, 2.4.7).
- Fields, checkboxes and buttons are at least 44 px tall (WCAG 2.5.5).
- No time limit, and if there is one, warn and allow extending it (WCAG 2.2.1).
- No tests that rely on memory or solving a puzzle to log in, unless there is another way (WCAG 3.3.8).
# Field
Fields and forms like the contact form of the portfolio: a glass panel, labels in bold, rounded fields with a gray fill, a ring while you type, and choices in a list.
## Complete form
```html
```
## Pieces
| Class | What it is |
| --- | --- |
| `.gm-form` | The glass panel of the form |
| `.gm-field` | A label with its field (or a `fieldset` with its options) |
| `.gm-field__help` | Help text, linked with `aria-describedby` |
| `.gm-field__error` | Error message |
| `.gm-input-group` | Two fields in a row: a third and two thirds (prefix and number) |
| `.gm-check` | A list of checkboxes or radio buttons |
| `.gm-form__actions` | The buttons at the end |
| `.gm-btn--accent` | The accent button (pink by day, blue at night) to send |
## Errors
Mark the field with `aria-invalid="true"` and link the message with `aria-describedby`. The message says what to do, not just that something is wrong.
```html
```
## Accessibility
- Every field has a visible label. Groups of checkboxes or radio buttons go in a `fieldset` with its `legend`.
- Field borders reach 3.5:1 in light mode and 3.9:1 in dark mode (3:1 is required).
- While you type, the field shows a ring and a pink border by day and blue at night (5.5:1 in light, 8:1 in dark; 3:1 is required for borders).
- Use `autocomplete` (name, email, tel…) so browsers can fill the form.
- Write “(required)” in the label of required fields, as well as `required`, and “(optional)” in the label of optional ones. More in [Form rules](/docs/forms/rules).
- The send button: white text on dark pink (8.6:1) by day and dark blue (11.3:1) at night, AAA.
# Segmented control
Options together in a gray bar; the chosen one sits in a white pill (black at night) with a shadow. It is the one from the My Book Box library.
## To pick a setting
With radio buttons inside a `fieldset` with its `legend`. The radio circle is hidden: the white pill shows which one is chosen. It changes with the mouse or the arrow keys.
```html
Preview
Light mode
Dark mode
```
## To filter, with a count
With buttons that say whether they are pressed (`aria-pressed`), inside a named group. Each option can show how many items there are (`.gm-segmented__count`).
```html
All
Books 3
Manga 20
```
## Stacked
With more than 3 options or long labels, one below the other: `.gm-segmented--stack`.
```html
Harmony
Monochromatic
Analogous
Complementary
Triadic
Split complementary
```
## Small
`.gm-segmented--small`, for toolbars or tight spaces.
```html
Recent
A-Z
```
## When to use it
- **2 or 3 short options**, in a row.
- **More than 3 or long labels**, stacked (`--stack`).
- For settings applied right away (view, filter, sort). In a form to submit, radios work too.
## Accessibility
- With radios: the `fieldset` and its `legend` say what is being chosen, and it works like any group of options (Tab to enter, arrows to change).
- With buttons: `role="group"` and `aria-label` on the bar, and `aria-pressed` on each button.
- The gray text reaches 7.5:1 by day and 7.1:1 at night on the bar (AAA); the chosen one, regular text.
- Each option is 34 px tall, but the touch area reaches 44 px (WCAG 2.5.5).
- The chosen one stands out by the pill and the shadow, not just color.
# Range
To pick a number in a range by dragging: volume, opacity, size… The thumb is a round dot and the filled part uses the accent color.
## Example
```html
Volume
```
## With the value shown
Show the value next to the label with an ``. That way the exact number is known without guessing from the position.
```html
```
## Disabled
```html
Brightness
```
## Script
Firefox paints the filled part on its own. Chrome and Safari do not: the optional script puts the value's percentage in `--gm-range-progress` every time the bar moves. Without the script the bar works the same, but all in gray.
```html
```
## Keyboard
| Key | What it does |
| --- | --- |
| ← → / ↑ ↓ | One step (`step`) down or up |
| Page Up / Page Down | 10 % up or down |
| Home / End | To the minimum or maximum |
## Accessibility
- It is a real ` ` with its ``: screen readers say the name and the value.
- If the bare number is unclear (“70”), add `aria-valuetext` with the value in words (“70 percent”).
- The dot is white with a 3.5:1 gray border, so it shows on white and dark backgrounds (WCAG 1.4.11). With the keyboard it gets a focus ring.
- The bar is 44 px tall so it is easy to press (WCAG 2.5.5, AAA), even though the visible line is thin.
- Dragging is not the only way: it also works with the keyboard or by clicking on the bar (WCAG 2.5.7).
# Date picker
A calendar with a glass panel, the month in large type and the days in 44 px circles. Today is in blue and the chosen day is filled.
## Example
Add `data-gm-calendar` and load the calendar script: it creates the calendar, changes the month and lets you choose a day with the mouse or the keyboard.
```html
```
## Days
| Day | How to mark it | How it looks |
| --- | --- | --- |
| Today | `aria-current="date"` | Blue and bold |
| Chosen | `aria-pressed="true"` | Black circle in light mode, white in dark mode |
| From another month | `.is-outside` | Gray |
| Not available | `disabled` | Paler, and it cannot be chosen |
## Options
| Attribute | What it does |
| --- | --- |
| `data-selected="2026-10-22"` | The chosen day |
| `data-month="2026-10"` | The month shown at the start |
| `data-today="2026-10-17"` | Which day is «today» (by default, the date of the device) |
| `data-locale="es"` | Language of the names (by default, the `lang` of the page) |
| `data-min`, `data-max` | First and last day that can be chosen |
When a day is chosen, the calendar sends the `gm-date-change` event with the date:
```js
document.querySelector('.gm-calendar').addEventListener('gm-date-change', (event) => {
console.log(event.detail.date); // '2026-10-22'
});
```
## Keyboard
| Key | What it does |
| --- | --- |
| ← → | Previous or next day |
| ↑ ↓ | Same day of the previous or next week |
| Home / End | First or last day of the week |
| Page Up / Page Down | Previous or next month |
| Enter / Space | Choose the day |
## Only with CSS
Without the script you can write the calendar by hand: the framework gives the look and your page decides what happens.
```html-code
Mon Tue Wed Thu Fri Sat Sun
28 29 30 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 1
```
## Accessibility
- Each day is a button with the full date in `aria-label`, so screen readers say «October 22, 2026» and not just «22».
- The days of the week use `` with the full name.
- The month buttons have `aria-label`.
- Days of 44 px, the size WCAG 2.5.5 (AAA) asks for, also on mobile.
- Contrast: numbers 15:1 or more, today in blue 5:1 or more, days from other months 5:1 or more (AA).
# Color picker
A button with the color swatch that opens a window to choose it: color wheel, hue, saturation, lightness and opacity bars, the color code and the contrast of white and black text on top. It is the one used by the tools on this site.
## Example
Press the button to open the window.
```html
```
## Without opacity
With `data-alpha="false"` the opacity bar goes away and the color is always opaque.
```html
```
## Script
The framework provides the look. The optional script builds the button and the window, and stores the color in the ` `:
```html
```
- The chosen color goes into the ` `'s `value` (`#rrggbb`, or `rgba()` with transparency), so it is sent with the form.
- Every change fires `input` and `change` on the ` `, and `gm-color-change` on the picker with the color in `detail.value`.
- Texts are in Spanish if the page has ``, and in English otherwise. They can be changed with `initColorPickers(document, { texts: { done: 'OK' } })`.
```js
document.querySelector('.gm-color-picker').addEventListener('gm-color-change', (event) => {
document.body.style.background = event.detail.value;
});
```
## Keyboard
| Key | What it does |
| --- | --- |
| Enter / Space | Opens the window (on the button) |
| ← → | On the wheel: changes the hue (with Shift, 10 at a time) |
| ↑ ↓ | On the wheel: changes the saturation |
| ← → on a bar | Moves the bar |
| Escape | Closes the window and focus returns to the button |
## Accessibility
- The window is a `` with a title; when it closes, focus returns to the button.
- The wheel is a `slider` that states hue and saturation in words (“Hue 186 degrees, saturation 79 %”).
- The bars are ` ` with their `` and value; the thumb is a round dot.
- If the typed code is not valid, the field says so in text, not just with color.
- The white and black text contrast helps choose colors that read well (WCAG 1.4.3).
# Accordion
A list of sections that open and close, all in one glass card and separated by a thin line. It is the one from the portfolio's skills section.
## Example
With the same `name` on every ``, only one opens at a time.
```html
How long does shipping take?
Between 2 and 4 working days on the mainland. To the islands, between 4 and 7.
Can I return an order?
Yes, within 30 days of receiving it, no questions asked.
Which payment methods do you accept?
```
## With icon and detail
Like in the portfolio: an icon in a circle on the left and a detail line under the title.
```html
Design
4 tools
Figma
Clip Studio Paint
Photoshop
Illustrator
Web development
6 technologies
HTML
CSS
JavaScript
TypeScript
Angular
.NET
```
## With preview and logo list
Like Skills in the portfolio template: closed, a few logos on the right (`.gm-accordion__preview`) and how many more there are; open, the list with a logo before each item, one below the other (`.gm-accordion__list--icons`). With `--cascade` and `style="--i: 0"`, `1`, `2`… the items come in one after the other when it opens.
```html
Frontend
5 technologies
+2
Angular
React
Astro
Tailwind CSS
WCAG (web accessibility)
```
On narrow screens the preview shows fewer logos (3 below 600 px) and disappears below 420 px. The logos are those in the Brand category of the [icons](/icons).
## Lists inside
If there are several items inside (more than 3), they go one below the other in a list, not run together in a sentence: ``. They are easier to scan and screen readers say how many there are.
## Several open at once
Without `name`, each section opens and closes on its own. With `open`, it starts open.
```html-code
…
```
## Accessibility
- It uses HTML `` and ``: they open with the mouse, Enter or Space, and screen readers say whether they are open or closed. No JavaScript needed.
- The chevron is decorative (`aria-hidden`); the state comes from the `` itself.
- The accordion border reaches 3.3:1 (WCAG 1.4.11) and each row is at least 44 px tall (WCAG 2.5.5).
- The animation is removed when the person asks for reduced motion (`prefers-reduced-motion`).
- Do not hide information everyone needs in an accordion: use it for details only some people want.
# Alert
A highlighted message inside the page: information, something that went well, a warning or an error. The state pastel at medium strength, with a border of the same color, regular text and an icon.
## Examples
```html
Version 0.1 is not on npm yet.
Changes saved.
You have 2 trial days left.
The form could not be sent. Check your connection and try again.
```
## Types
| Class | For | Color |
| --- | --- | --- |
| `.gm-alert` | Information | Gray |
| `.gm-alert--success` | Something went well | Mint |
| `.gm-alert--warning` | Warning | Lemon |
| `.gm-alert--danger` | Error | Coral |
## Accessibility
- Color is not enough: the icon and the text also say what kind of alert it is (“Changes saved”, “Could not…”) (WCAG 1.4.1).
- If the alert appears after the person does something, add `role="status"` (calm messages) or `role="alert"` (errors): screen readers read it without moving focus (WCAG 4.1.3).
- The icon is decorative (`aria-hidden="true"`).
- Text of 7:1 or more on its background in both modes (AAA).
# AI notice
A small note next to a chat or anything made with AI, so people know the answers come from an AI and can be wrong. With a link to more information.
## Example
```html
Answers generated with AI from my profile; they may contain mistakes.
More information
What it is An AI model that answers from my profile. It can be wrong: for anything important, write to me.
```
“More information” opens a [dialog](/docs/components/dialog) explaining which AI it is, what happens to what people write and how to ask for it to be deleted.
## Why
- The **EU AI Act** (article 50) requires telling people they are talking to an AI.
- Data protection also asks for information about what is stored and what is not.
## Accessibility
- Gray text of 7:1 or more on the background (AAA), small but readable.
- The link sits inside the sentence, underlined, and is a button because it opens a dialog (it does not go to another page).
# Avatar
The profile picture box, as a circle or a square with rounded corners. Without a photo, it shows the initials on a pastel or a silhouette.
## Shapes
Circle (`.gm-avatar`) or rounded square (`.gm-avatar--square`). The photo is cropped to fill the box without stretching.
```html
```
## Sizes
`.gm-avatar--sm` (2 rem), default (3 rem), `.gm-avatar--lg` (5 rem) and `.gm-avatar--xl` (8 rem). The rounding and the letters grow in proportion.
```html
```
## Without a photo
With initials: add a palette color (`.gm-theme-sky`, `.gm-theme-mint`…) and give the name with `role="img"` and `aria-label`, because initials alone are not clear. Without a name, a silhouette.
```html
SP
MG
DS
```
## With a ring
`.gm-avatar--ring`: the photo with a colored ring, like the one in the portfolio template. By default, with the lights of the illustration; the thickness is proportional to the size.
```html
```
## Customize
| Variable | What it changes | Default |
| --- | --- | --- |
| `--gm-avatar-size` | The size | 3 rem |
| `--gm-avatar-radius` | How rounded the corners are | 50% (circle); 22% with `--square` |
| `--gm-avatar-ring` | The ring color (a color or a gradient), with `--ring` | The lights of the illustration |
```html
```
## Enlarge on click
To show the photo large when it is pressed: the box is a link to the large photo with `data-gm-zoom`, and the [gallery](/docs/components/gallery) script opens it in its viewer, without arrows or counter. A magnifier cursor shows on hover.
```html
```
```html-code
```
The viewer closes with Escape, the ✕ or by clicking outside, and focus returns to the photo.
## Changing the photo
On the profile page, a button with the pencil on top of the photo. Use it with the large sizes (`--lg` and `--xl`): the button is 44 px.
```html
```
## Group
Several stacked photos, for example to show who is taking part. The last one can say how many more there are.
```html
```
## Accessibility
- A photo with the name next to it (in a list of people) is decorative: `alt=""`. On its own, it carries the name: `alt="Lucía Martín"`.
- Initials go with `role="img"` and an `aria-label` with the full name.
- In a group, the group's `aria-label` says who takes part and the photos have `alt=""`.
- Initials are black on the pastel (7.8:1 or more, AAA); without a color, regular text on gray.
- The button to change the photo is 44 px (WCAG 2.5.5) and says what it does.
# Button
Buttons for actions in forms, dialogs and pages, with the rounded shape of My Book Box. All of them meet WCAG 2.2 AA in light and dark mode.
## Variants
`.gm-btn` is the main button: black in light mode and white in dark mode. Add `.gm-btn--secondary` for a glass button with a border, or `.gm-btn--ghost` for one without background.
```html
Primary
Secondary
Ghost
```
## With color
Add a color of the palette (`.gm-theme-sky`, `.gm-theme-mint`…) and how to use it: `.gm-theme-solid` for the pastel with black text, or `.gm-theme-glass` for the glazed version. The button keeps its pill shape.
```html
Solid
Glazed
```
All the colors, solid:
```html
Sky
Blue
Lavender
Pink
Peach
Lemon
Mint
Coral
```
And glazed:
```html
Sky
Blue
Lavender
Pink
Peach
Lemon
Mint
Coral
```
The text on the pastel is always black (7.8:1 or more, AAA). Never use white text on these colors. To send a form, the accent `.gm-btn--accent` (pink by day, blue at night) of [Field](/docs/forms/field) is also available.
## Small
`.gm-btn--sm` makes it lower (38 px) and with smaller text.
```html
Primary
Secondary
```
## With an icon
Put an icon before or after the text. It is decorative, so it has `aria-hidden="true"`.
```html
Download
Next
```
## Icon only
`.gm-icon-btn` is a round 42 px button. As it has no text, it needs `aria-label` with what it does.
```html
```
## Pill
`.gm-btn--pill` is a small button with the gray of the tags, for secondary actions in lists and cards. Add `.gm-btn--danger` for actions that delete.
```html
Edit
Delete
```
## Text
`.gm-btn-text` looks like a link but is a button, for small actions inside a text.
```html
Show more
```
## Back
`.gm-btn-back` goes back to the previous section. The arrow is decorative; if the visible text does not say where it goes, add it with a visually hidden text.
```html
```
## Links that look like buttons
The classes also work on ``. Use ` ` when it goes to another page and `` when it does something on the same page.
```html
```
## Disabled
With the `disabled` attribute the button is paler and cannot be pressed.
```html
Primary
Secondary
```
## Accessibility
| Button | Contrast (light / dark) | Level |
| --- | --- | --- |
| `.gm-btn` | 16.8:1 / 15.5:1 | AAA |
| `.gm-btn--secondary`, `--ghost` | 16.6:1 / 13.9:1 | AAA |
| `.gm-btn--pill` | 13.8:1 / 12.8:1 | AAA |
| `.gm-btn--danger` | 9.6:1 / 9.8:1 | AAA |
| `.gm-btn-text` | 7:1 / 7:1 | AAA |
- The focus is always visible: a 2 px outline when you move with the keyboard.
- The area you press always measures at least 44 × 44 px (WCAG 2.5.5, AAA): in small buttons and pills an invisible layer makes it bigger without changing the look.
- A button with only an icon always needs `aria-label`.
- Disabled buttons are exempt from contrast, but explain why the action is not available when it is not obvious.
# Card
A glass box to group content: translucent background with blur, thin border, rounded corners and a soft shadow. It is the My Book Box panel.
## Example
```html
Basic plan
To get started: a one-page website with a contact form.
See details
```
## In a grid
With the [grid](/docs/layout/grid) (`.gm-grid--auto`), as many cards per row as fit.
```html
Light and dark Both modes, built in.
Accessible WCAG 2.2 AA and AAA.
```
## The whole card as a link
If the whole card leads to another page, make the card the ``. That avoids several repeated links.
```html
Palette generator
Create an accessible palette from one color.
```
## Accessibility
- The border and shadow separate the card from the background; important content does not depend on them.
- If the card is a link, its text must say where it goes (WCAG 2.4.4).
- The last element in the card has no bottom margin, so the padding is the same top and bottom.
# Carousel
Images shown one at a time, inside a glass frame, with arrows on the sides and dots below. It is the one from the portfolio's projects section.
## Example
Move through the slides with the arrows, the dots, by swiping or with the ← → keys.
```html
SunriseFirst light over the mountains.
HillsThree layers of hills at sunset.
ValleyThe valley in spring.
```
## Autoplay
With `data-interval` (in milliseconds), it moves on its own and shows a pause button. It stops on hover or when using the keyboard inside, and does not autoplay if the person asks for reduced motion.
```html-code
…
```
## Without text
The caption (`.gm-carousel__caption`) is optional.
```html-code
```
## Without buttons
With `.gm-carousel--peek` and no `data-gm-carousel`: photos move by swiping with a finger, the mouse or the arrow keys, and the next one peeks in from the side to show there is more. Give the track `tabindex="0"` and a name so it works with the keyboard.
```html
Sea of clouds
Sunset
Dusk
```
## Swipe row
Like the quick questions of the portfolio chat: everything in one row that slides sideways with a finger, the mouse or the keyboard, with no arrows or dots. The right edge fades out to show there is more. Use `.gm-scroll-row`; no script needed.
```html
Which technologies do you use?
How much does a website cost?
Do you build online shops?
How long does it take?
```
With `.gm-scroll-row--mobile`, it only slides on phones (767 px or less); on large screens, the items wrap to the next line.
```html-code
…
```
## Script
The framework provides the look. Without the script, slides move by swiping. The optional script adds the arrows, the dots and autoplay:
```html
```
When the slide changes, it fires `gm-carousel-change` with its number (starting at 0) in `detail.index`.
## Accessibility
- The carousel is a named region (`aria-label`) and each slide says which one it is (“2 of 3”).
- Arrows and dots have names (“Next slide”, “Go to slide 2”) and are 44 px tall (WCAG 2.5.5). The active dot stands out by its longer shape, not just color.
- Slides that are not visible do not get focus (`inert`).
- With autoplay, there is a pause button and it stops with the mouse or the keyboard (WCAG 2.2.2). While autoplaying, screen readers do not announce every change.
- The caption is white with a shadow on a black gradient, so it reads over any image. Every image has its `alt`.
- Do not put the only important thing in a carousel: many people only see the first slide.
- In the swipe row, every item is reachable with Tab and the row scrolls to it on its own.
# Chat
The chat pieces from the AI portfolio template: the field to ask, the answer bubble and the typing dots. The chat itself (asking the AI, writing the answer letter by letter) comes from your app; the framework provides the look.
## Field to ask
A glass pill with the field and the send button. While typing, the border takes the lights and a soft ring appears.
```html
Write a question
```
## Answer bubble
Glass with the lights border and, in dark mode, a turquoise glow. The tail points to the speaker: `.gm-bubble--tail-end` (right) or `.gm-bubble--tail-start` (left), at the height of `--gm-bubble-tail-y` (measured from the bottom on the right one; on the left one, from the top, or from the bottom with `.gm-bubble--tail-bottom`).
```html
Picking up where we left off When do you need the website?
```
## Thinking
While the answer arrives, `.gm-typing` with three ``s: three dots bouncing with the lights of the illustration.
```html
Thinking about the answer…
```
## Error
`.gm-bubble--error`: the border in coral, the error color.
## Change the color
With variables, on the bubble or on any element that contains it:
| Variable | What it changes | Default |
| --- | --- | --- |
| `--gm-bubble-color` | The border and the tail of the bubble (a color or a gradient) | The lights of the illustration |
| `--gm-bubble-tail-color` | Only the tail, if the border is a gradient (the tail takes a color, not a gradient) | The one in `--gm-bubble-color` |
| `--gm-typing-color` | The "thinking" dots | The lights of the illustration |
```html
With the accent color: pink by day and blue at night.
With a gradient from mint to sky; the tail, in mint.
Thinking about the answer…
```
## Accessibility
- The field has its `` (visually hidden) and the send button its name.
- If text is written letter by letter, it is decorative: announce the whole answer at once in a separate `aria-live="polite"`.
- If the answer does not fit, the text scrolls inside; with `tabindex="0"` and `role="region"` it can be moved with the keyboard (WCAG 2.1.1).
- Next to the chat, add the [AI notice](/docs/components/ai-notice): people must know they are talking to an AI.
# Chip
A glass pill for quick options, like the suggested questions of a chat. It is the one from the AI portfolio template: the same glass as the accordion and, on hover, it rises a little and the border takes the lights of the illustration (turquoise in dark mode).
## Example
```html
Which technologies do you use?
How much does a website cost?
Do you build online shops?
I want a project
```
## Accent
`.gm-chip--accent` always has the lights border and semibold text: for the main action of the group.
## On phones
Many chips in a row fit better in a [swipe row](/docs/components/carousel#swipe-row), as in the template:
```html
Which technologies do you use?
How much does a website cost?
Do you build online shops?
```
## Change the borders
| Variable | What it changes | Default |
| --- | --- | --- |
| `--gm-chip-border` | The border at rest | The panel gray |
| `--gm-chip-color` | The border on hover and on the accent chip (a color or a gradient) | The lights of the illustration |
With the accent color at rest and on hover, and with a pastel gradient on hover:
```html
What technologies do you use?
How much does a website cost?
I want a project
```
## Without a border
`.gm-chip--borderless`: only the glass, with no border at rest or on hover (it still rises a little). Use it on backgrounds where the glass stands out well.
```html
What technologies do you use?
Do you build online shops?
```
## Disabled
With `disabled`, or with `aria-disabled="true"` if it must stay focusable (for example, inside a swipe row while the AI is thinking).
## Accessibility
- They are `` elements with text: they say what they do.
- They look smaller than 44 px, but the touch area reaches 44 px (WCAG 2.5.5).
- Text reaches 7:1 on the glass in both modes; the border, 3:1. If you change the border color, keep it at 3:1; without a border, the text is enough to tell what it is.
# Dialog
A window that opens on top of the page to show information or ask for something, with the background dimmed behind it. It is the “About the AI” one from the portfolio template: the panel glass, sections separated by a thin line and colored headings.
## Example
```html
Open the dialog
What it is
The chat uses an AI model that answers from my profile and can be wrong .
What happens to what you write
Quick questions
Your question is only used to give you the answer. It is not stored.
“I want a project”
The assistant asks you a few questions about your project.
At the end, it shows you a summary and offers to send it to me.
If you do not send it, nothing reaches me.
```
## Parts
| Class | What it is |
| --- | --- |
| `.gm-dialog` | The window (an HTML ``) |
| `.gm-dialog__body` | The content; scrolls if it does not fit |
| `.gm-dialog__header` | Title and close button |
| `.gm-dialog__title` | The title; it gets focus when opened |
| `.gm-dialog__section` | Each section, with a line above; its ``s are colored |
## Script
The optional script opens the dialog with `data-gm-dialog-open="id"` and closes it with `data-gm-dialog-close`, Escape or clicking outside:
```html
```
## Accessibility
- It is an HTML `` opened with `showModal()`: the rest of the page becomes inert, so neither the mouse, the keyboard nor screen readers leave the dialog while it is open.
- It is named with `aria-labelledby` (its title) and screen readers announce it as a dialog.
- When opened, focus goes to the title, not the ✕, so it is read from the start. When closed, it returns to the button that opened it.
- Escape always closes it, and the close button says what it does (“Close”).
- Use it sparingly: it interrupts. For short messages, an [alert](/docs/components/alert) on the page is better.
# Footer
The footer from the portfolio template: a heading, icon links (email and social), the legal links on one line and the name with the year. Centered, with a thin line above.
## Example
```html
```
## Parts
| Class | What it is |
| --- | --- |
| `.gm-footer` | The footer, centered with a line above |
| `.gm-footer__socials` | 32 px icon links (email, social) |
| `.gm-footer__links` | Legal links on one line, underlined and gray |
| `.gm-footer__bottom` | The year and the name (in ``) |
| `.gm-footer__credit` | Credits, smaller |
## Large social icons
For email and social links outside the footer (for example, on the home page): `.gm-socials`, 36 px icons that grow a little on hover. Change the size with `--gm-socials-size`.
```html
```
## Brand logos
GitHub and LinkedIn are in the GlassMarshmallow icons (`gmi-github`, `gmi-linkedin`), with their official shape: brands do not allow their logo to be redrawn. For other brands, use their official SVG or the one from [Simple Icons](https://simpleicons.org) (free license), with `fill="currentColor"` so it takes the text color.
## Accessibility
- Icon-only links have an `aria-label` (“Send an email”, “GitHub, opens in a new tab”).
- Each link is at least 44 px (WCAG 2.5.5).
- The legal links go in a named ``.
# Photo gallery
An album: photos in a grid, all the same size, with more columns on large screens and fewer on phones. Pressing one shows it large in a viewer with arrows to move to the next.
## Example
```html
```
## Square and with text
`.gm-gallery--square` crops photos to squares. For a text under each one, add `.gm-gallery__caption` inside the link.
```html
```
## Larger photos
`.gm-gallery--lg`: columns of at least 300 px instead of 200 px.
```html-code
…
```
## Script
The framework provides the look. Without the script, each photo opens the large image in the same tab. The optional script shows it in the viewer:
```html
```
- Each photo's `href` is the large image; the thumbnail (` `) can be a lighter version.
- The viewer text comes from `data-caption` or, if missing, from the thumbnail's `alt`.
## A single photo
To enlarge a single photo (for example, the [profile photo](/docs/components/avatar#enlarge-on-click)): a link to the large photo with `data-gm-zoom` and `initZooms()`. The viewer shows without arrows or counter.
## Keyboard
In the viewer:
| Key | What it does |
| --- | --- |
| ← → | Previous or next photo |
| Home / End | First or last photo |
| Escape | Closes the viewer and focus returns to the photo |
## Accessibility
- Each photo is a link with its `alt`, so it opens with the keyboard and screen readers say what it is.
- The viewer is a ``: focus stays inside and Escape closes it. The counter (“2 of 9”) is announced when the photo changes.
- Arrows and close have names and are 44 px (WCAG 2.5.5); on a nearly black background, with white border and focus.
- The gentle zoom on hover is removed when the person asks for reduced motion.
# Tag
A small pill to label something: a category, a status, something new. It is the gray My Book Box tag.
## Example
```html
Novel
Fantasy
2026
```
## With color
With the palette colors (`.gm-theme-…` and `.gm-theme-solid`), black text on the pastel.
```html
New
Read
Pending
Sold out
```
## Tag or button
A tag only informs: it is not pressed. To filter or choose, use a pill button (`.gm-btn--pill`) or the [segmented control](/docs/forms/segmented).
## Accessibility
- The tag text says what it means; color only reinforces it (WCAG 1.4.1).
- Regular text on gray (14:1) or black on pastel (7.8:1 or more): AAA.
- It does not look like a button (no border, no hover effect), to avoid confusion.
# Navbar
The top bar, like the one in My Book Box and My MTV Box: logo and name on the left, the section links and, on the right, the actions (language, theme…). It is glass and sticks to the top when scrolling.
## Example
On the site it sticks to the top (`position: sticky`); here it is shown inside the frame.
```html
```
## Parts
| Class | What it is |
| --- | --- |
| `.gm-navbar` | The bar: glass, sticky, as wide as the container |
| `.gm-navbar__brand` | Logo and name (link to home) |
| `.gm-navbar__logo` | The logo, 48 px (42 px on phones) |
| `.gm-navbar__nav` | The section links; the current page has `aria-current="page"` and is bold. On a one-page site, the section being viewed has `aria-current="location"` |
| `.gm-navbar__actions` | Buttons on the right (language, theme, account…) |
## Without a logo and floating on phones
The bar of the portfolio template. **On computers**, without a logo: the links in the center and the theme button on the right (`.gm-navbar--centered`). **On phones**, no bar: only the round glass buttons remain, floating at the top right (`.gm-navbar--floating`), and the menu button (`.gm-navbar__menu`) opens the [full-screen menu](/docs/components/fullscreen-menu).
```html
```
This is how it looks on phones (make the window narrower than 860 px to see it in the top bar):
```html
```
## Change the colors
With variables on the bar:
| Variable | What it changes |
| --- | --- |
| `--gm-header-bg` | The background (a color, with transparency if you want glass) |
| `--gm-header-filter` | The glass blur (`blur(24px)`; `none` to remove it) |
| `--gm-header-border` | The bottom line |
| `--gm-header-shadow` | The shadow |
| `--gm-nav-size` | The link size (0.88 rem; 1 rem in the bar without a logo) |
| `--gm-nav-link` | The link color |
| `--gm-nav-link-hover` | The link color on hover (a color or a gradient). By default, the accent: the strong one by day (dark pink, 8.6:1) and the normal one at night (blue, or turquoise with `gm-accent-turquoise`) |
So the text reads well on the new background, the easiest way is to fix the mode of the bar with `data-theme`: a dark bar on a light page (`data-theme="dark"`) or a pastel with black text (`data-theme="light"`).
```html
```
### On hover
Hover over the links: in the first bar they take the accent color (pink by day, blue at night); in the second, a gradient from lavender to pink.
```html
```
To change the accent color of the whole site (also buttons, focus and fields), see [Another accent color](/docs/getting-started/css-variables#another-accent-color).
For the whole site, put the variables in `:root` (and the night ones in `[data-theme="dark"]`), as in [CSS variables](/docs/getting-started/css-variables).
## On phones
Below 860 px, the `.gm-navbar__nav` links are hidden and move to the [tab bar](/docs/components/tabbar) at the bottom. Between 860 and 1024 px the name next to the logo is hidden so everything fits.
## Accessibility
- Links go in a named `` (`aria-label="Main"`) and the current page is marked with `aria-current="page"`, which also shows in bold.
- Each link is 44 px tall (WCAG 2.5.5).
- On hover or press, links take the accent color: dark pink by day and blue at night, both 7:1 or more (AAA).
- Focused items are not hidden behind the bar: the page leaves room when scrolling (WCAG 2.4.12).
# Tab bar
The phone's bottom bar, floating and glass, like in iOS and My Book Box. It replaces the [navbar](/docs/components/navbar) links below 860 px.
## Example
On the site it only appears on phones and floats over the page (`position: fixed`); here it is shown inside the frame.
```html
Home
Docs
Icons
More
```
## “More” panel
What does not fit in the bar goes in a panel opened with the “More” button (`.gm-tabbar-more`, with `.is-open` to show it).
```html-code
Tools
Blog
```
## Rules
- **4 buttons at most, counting “More”**: with more, labels do not fit and buttons get too small.
- Each button has an icon and a short label (one word).
- The page leaves a gap at the end so the bar does not cover content: add `.gm-has-tabbar` to the `` or the page container.
## Accessibility
- It is a named ``; the current page has `aria-current="page"` and shows in the text color (the rest in gray).
- “More” is a button with `aria-expanded` and `aria-controls`; Escape closes the panel.
- Focused items are not hidden behind the bar (WCAG 2.4.12).
# Fullscreen menu
The mobile menu from the portfolio template: a round glass button with the hamburger opens the links in large type, filling the whole screen over a nearly opaque background. It is another way to navigate on phones, for sites with few sections; the other is the [tab bar](/docs/components/tabbar).
## Example
```html
```
## Round glass button
`.gm-icon-btn--glass` is the template's 44 px glass circle, meant to sit on an illustration or an image: the menu button, the close button and the light/dark switch.
## Script
```html
```
The button that opens it is linked to the menu with `aria-controls="menu-id"`.
## Accessibility
- When closed, the menu is inert: it cannot be reached with the keyboard nor read by screen readers.
- When open, the rest of the page is inert, focus goes to the first link and the button says `aria-expanded="true"`.
- It closes with Escape, the ✕ or by choosing a link, and focus returns to the menu button.
- Large links (2 rem) at least 44 px tall; the current page with `aria-current="page"`.
# Menu
A button that opens a list of options, like the language, version and theme menus of the top bar. One option can be marked as chosen.
## Example
Open it with the mouse or with the keyboard (Enter or Space on the button).
```html
```
## Aligned to the left
The list opens aligned to the right of the button. Next to the left edge, add `.gm-menu--start`.
```html
```
## Script
The framework gives the look. To open, close and move with the keyboard, load the optional script:
```html
```
When an option is chosen, the menu sends the `gm-menu-select` event with the `data-value` of the option:
```js
document.querySelector('.gm-menu').addEventListener('gm-menu-select', (event) => {
console.log(event.detail.value); // 'dark'
});
```
## Keyboard
| Key | What it does |
| --- | --- |
| Enter / Space | Opens the menu (on the button) or chooses the option |
| ↓ ↑ | Next or previous option |
| Home / End | First or last option |
| Tab | Goes around the options without leaving the menu |
| Escape | Closes the menu and goes back to the button |
## Accessibility
- The button has `aria-haspopup="menu"`, `aria-expanded` and an `aria-label` that says what is chosen («Theme: Auto»).
- The options are `menuitemradio` with `aria-checked`; the check icon is only visual.
- Button and options measure 44 px (WCAG 2.5.5, AAA). Text 15:1 or more.
# Search
A text field with the magnifier inside and, below it, the results that match what is typed. It is the search box in the top bar of this site.
## Example
Type, for example, “color” or “button”.
```html
```
## Width
The search box takes the full width of its container. To limit it, give `.gm-search` a width (in the example, `max-width: 320px`). The results list is as wide as the field, with a minimum of 320 px; on screens of 600 px or less, exactly as wide as the field.
## Script
The framework provides the look. To filter while typing and move with the keyboard, load the optional script:
```html
```
- Each result is an ``. The search looks at its text and, if present, `data-keywords` (ignoring case and accents).
- With `data-href`, choosing it goes to that address. Without it, only the event fires.
- If there is a `.gm-search__empty` inside the list, it shows when there are no results.
- The element with `data-gm-search-status` announces how many results there are; the text goes in `data-results-text`, with `{count}`.
When a result is chosen, the search box fires the `gm-search-select` event with its `data-value` and `data-href`. If you cancel it with `preventDefault()`, the page does not change (useful in apps with their own router):
```js
document.querySelector('.gm-search').addEventListener('gm-search-select', (event) => {
event.preventDefault();
router.navigate(event.detail.href);
});
```
## Keyboard
| Key | What it does |
| --- | --- |
| ↓ ↑ | Next or previous result |
| Enter | Opens the highlighted result (or the first one) |
| Escape | Closes the list; if already closed, clears the text |
## Accessibility
- It follows the WAI-ARIA combobox pattern: the field has `role="combobox"`, `aria-expanded`, `aria-controls` and `aria-activedescendant`, and the list is a `listbox` with its `option`s.
- The field has its `` (hidden from view, not from screen readers) and `role="search"` marks the area as a search.
- The number of results is announced with `aria-live`.
- Field and results are 44 px tall (WCAG 2.5.5, AAA). The highlighted result has a background and a border, not just color.
# Video player
The My MTV Box player: controls appear over the video on a black gradient, the buttons are round glass and the play button is white. While the video plays, the controls hide on their own.
## Example
Press play, try the subtitles (CC) and full screen.
```html
```
## Without a title
Without `data-title`, only the video and the controls show.
```html-code
```
## Size
The player takes the full width of its container with a 16:9 ratio. For another format, change `aspect-ratio`:
```html-code
…
```
## Script
The framework provides the look. The optional script removes the browser controls and adds the framework ones. Without the script, the video keeps the browser controls (that is why the `` has `controls`).
```html
```
- Buttons: play and pause, 10 seconds back and forward, sound with its volume, the time, subtitles and full screen.
- Subtitles come from the video's `` (or `captions`) elements, in WebVTT format. If there are none, the button does not show.
- `data-title` and `data-subtitle` put the title on top, like the movie name and the episode.
- Texts are in Spanish if the page has ``, and in English otherwise. Change them with `initPlayers(document, { texts: { play: 'Watch' } })`.
## Keyboard
With focus inside the player:
| Key | What it does |
| --- | --- |
| Space / K | Play or pause |
| ← → | 10 seconds back or forward |
| ↑ ↓ | Volume up or down |
| M | Mute or unmute |
| C | Turn subtitles on or off |
| F | Full screen |
With the mouse too: a click on the video plays or pauses, and a double click goes full screen.
## Accessibility
- Every button has a name (“Play”, “Back 10 seconds”…) and is 44 px, also on phones (WCAG 2.5.5, AAA).
- The timeline is a slider with its name and says the time in words (“1:15 of 3:20”).
- Controls come back with the mouse, on touch or with the keyboard, and do not hide while paused.
- Add subtitles to videos with speech (WCAG 1.2.2) and do not autoplay them with sound (WCAG 1.4.2): the script does not.
- Always dark, with the controls on a black gradient, so they read over any image.
# Progress
How much of something is done: reading a book, uploading a file, the steps of a form. A thin line on gray with the done part in the accent color and a text below. It is the reading bar from My Book Box.
## Example
```html
45 %
45 % read · page 162 of 360
```
## Large
`.gm-progress--lg`, when the bar is the main thing (an upload, an installation).
```html
72 %
Uploading “novel.epub”: 72 %
```
## Unknown duration
Without `value`, the bar shows a segment moving back and forth: work is happening, but how much is left is unknown.
```html
Loading
Loading the library…
```
## Changing the value
It is an HTML ``: change it with its `value`.
```js
document.querySelector('#read').value = 60;
```
## Accessibility
- Use `` with its `max` and `value`: screen readers say the percentage. The `` gives it a name.
- Also write the data in the text (“45 % read”): the bar alone does not give the exact number.
- The done part reaches more than 3:1 on the gray in both modes (WCAG 1.4.11), and the gray text 7:1 (AAA).
- If progress changes on its own (an upload), announce the end with a status message (`role="status"`), not every change.
- No motion if the person asks for it (`prefers-reduced-motion`).
# Logo ticker
A strip that slowly moves from right to left with logos and names, like the technologies strip of the portfolio template. Dark glass (white by day) with a thin turquoise border above and below (by day, with the lights of the illustration).
## Example
The content goes **twice in a row** inside `.gm-ticker__track`, so the loop is seamless.
```html
Angular TypeScript PostgreSQL Figma Ollama
Angular TypeScript PostgreSQL Figma Ollama
```
## Static
`.gm-ticker--static`: no movement, with the logos that fit centered (for example, when the page has no animations). Then one copy is enough.
```html
Angular TypeScript PostgreSQL Figma Ollama
```
## Pausing it
The round button with the pause icon (`.gm-ticker__pause`) goes at the end of the strip, without taking up space. With the optional script, pressing it stops the strip (`.is-paused`), the icon changes to play and the button says it is pressed (its name, “Pause the strip”, does not change):
```html-code
```
It also stops on its own on hover or with focus inside, and it does not move if the person asks for reduced motion.
## Accessibility
- The logos are **decorative** (`aria-hidden="true"` on `.gm-ticker__track`): the same information must be elsewhere on the page (in the template, the Skills section).
- It moves on its own for more than 5 seconds, so it has the button to stop it, reachable with the keyboard and by touch (WCAG 2.2.2). When static (`--static`) it is not needed.
- It does not move if the person asks for reduced motion (WCAG 2.3.3).
# Tooltip
A small bubble with extra information about a button or a control. It uses the style of the reading bubble of My Book Box: dark background, white text and a soft shadow, in light and dark mode.
## How it works
Wrap the element and its bubble in `.gm-tooltip`. The bubble has `role="tooltip"` and an `id`, and the element points to it with `aria-describedby`, so screen readers read it.
It shows when you hover the element and when you reach it with the keyboard. Move the pointer onto the bubble and it stays open.
```html
Search the library
Download the book
Settings
```
## Below
By default it goes above. Add `.gm-tooltip--bottom` to put it below.
```html
Share with a friend
```
## Next to an edge
Centered bubbles can go past the edge of the screen. Next to the left edge, add `.gm-tooltip--start`; next to the right one, `.gm-tooltip--end`.
```html
Back to the library
Reader settings
```
## On a button with text
A tooltip adds information; it does not replace the name of the button. On a button with text, use it to explain something more.
```html
Publish
Everyone with the link will see it
```
## Close it with Escape
WCAG 2.2 asks that a tooltip can be closed without moving the pointer or the focus. As the framework is only CSS, add this small script to your page so that the Escape key closes it:
```js
// Escape hides the open tooltip until the pointer or the focus leaves it
document.addEventListener('keydown', (event) => {
if (event.key !== 'Escape') return;
document.querySelectorAll('.gm-tooltip:hover, .gm-tooltip:focus-within')
.forEach((tip) => tip.classList.add('is-dismissed'));
});
for (const type of ['mouseout', 'focusout']) {
document.addEventListener(type, (event) => {
const tip = event.target.closest?.('.gm-tooltip');
if (tip && !tip.contains(event.relatedTarget)) tip.classList.remove('is-dismissed');
});
}
```
## Accessibility
- White text on #1d1d1f: 16.8:1 (AAA), in light and dark mode.
- It shows with the pointer and with the keyboard, it stays open while the pointer is on it, and it closes with Escape (with the script).
- Don’t put links or buttons inside: a tooltip is only text. For interactive content, use a menu or a dialog.
- On touch screens there is no hover, so don’t put essential information only in a tooltip.
- With «reduce motion», it appears without animation.
# Visually hidden
`.gm-visually-hidden` hides text visually but keeps it for screen readers. Use it to give context that the design shows but cannot be read.
## Example
An icon-only button: sighted people see the cross; screen reader users hear “Delete photo”.
```html
Delete photo
```
## When to use it
- Field labels that the design makes clear (a search box with the magnifier).
- Section headings that are not shown but help moving around with a screen reader.
- Announced messages (`aria-live`) that take no space.
## When not to
- To hide something from everyone: use `hidden` or `display: none`.
- To hide something only from screen readers (a decorative icon): use `aria-hidden="true"`.
- If the text would help everyone, better to show it.
# Skip link
A link at the start of the page that goes straight to the content, skipping the menu. It is hidden until reached with the Tab key.
## Example
Press Tab when opening any page on this site: it is the first thing that appears, at the top left.
```html-code
Skip to content
…
```
## How to add it
- Make it **the first thing** inside ``.
- The `href` points to the ``'s `id`.
- The `` has `tabindex="-1"`, so focus really reaches the content.
## Why
Keyboard users would otherwise have to go through every menu link on every page. With this link they skip them at once (WCAG 2.4.1). When focused it appears as a black button (white at night), clearly visible.
# Gradient text
Paints text with the framework's color gradient. At night it switches to a plain color, so it reads well on a dark background.
## Example
```html
Made with GlassMarshmallow
Gradient heading
```
## Two versions
| Class | For | Gradient |
| --- | --- | --- |
| `.gm-gradient-text` | Text of any size | Dark tones, each 7:1 or more (AAA) |
| `.gm-gradient-title` | Large headings (24 px or more) | The bright logo colors, 3:1 or more |
At night, `.gm-gradient-text` is blue and `.gm-gradient-title` uses the text color.
## Accessibility
- A gradient cannot be measured point by point: that is why the text one only uses tones that pass AAA on their own.
- With system high contrast on, the text switches to the system color.
- Do not use it on long paragraphs: it is to highlight a few words or a heading.
# Text outline
`.gm-text-outline` adds, **in dark mode**, a thin black outline and a soft shadow to text. It keeps text readable over lights, images or the TV effect. In light mode it changes nothing.
## Example
This is how it looks over a background with lights in dark mode:
```html
```
It is the one used by the title and text of this site's home page.
## Accessibility
- The outline helps text reach its contrast even when the background changes from point to point (WCAG 1.4.3).
- Even so, measure the contrast over the real background (with a screenshot), not just the background color.
# User experience
Rules so that what you build with GlassMarshmallow is easy to understand and use. Some are ours (we decided them while designing the framework) and others come from reference guides (GOV.UK, W3C, Nielsen Norman Group). For forms, see the [form rules](/docs/forms/rules) too.
## Options to choose from
- **2 or 3 short options, in a row**, with the [segmented control](/docs/forms/segmented). **More than 3 or long labels, one below the other** (`--stack`).
- The chosen option shows by more than color: the white pill and the shadow.
- If there is a recommended or most common option, select it from the start.
## Lists
- **With more than 3 items, use a list**, one below the other, not run together in a sentence (“Figma, Photoshop, Illustrator and…”). They are easy to scan and screen readers say how many there are.
- Inside the accordion, with [`.gm-accordion__list`](/docs/components/accordion).
## Mobile navigation
- The [tab bar](/docs/components/tabbar) has **4 buttons at most, counting “More”**. What does not fit goes in the “More” panel.
- Each button with an icon and one word.
- The current page is marked in the bar (`aria-current="page"`).
## Icons
- **Only GlassMarshmallow icons** (`gmi`): no emojis and no icons from other sets mixed in.
- Next to text, **centered with its first line and a bit larger than the letters** (for example, 1.25 rem with 0.92 rem text). Checked by measuring, not by eye.
- An icon on its own (no text) has a name for screen readers (`aria-label` or hidden text). With text, the icon is decorative (`aria-hidden="true"`).
## Buttons and actions
- **One main action per area**: the black (white at night) or accent button (pink by day, blue at night). The rest, secondary or text buttons.
- The button text says **what happens when pressed** (“Save changes”, “Watch the next episode”), not “OK”.
- Actions that cannot be undone (delete, pay) ask for confirmation and say what will be lost.
- Everything that can be pressed is at least **44 px** (WCAG 2.5.5).
## Knowing what is happening
- **While loading**, show that work is happening: [progress bar](/docs/components/progress), spinning icon or placeholders with `gm-animate-shimmer`. With text (“Loading the library…”).
- **When done**, confirm what happened with an [alert](/docs/components/alert) (“Changes saved”), announced to screen readers (`role="status"`).
- **With no content**, explain why the screen is empty and what to do (“You have no books yet. Add the first one”).
- **If something fails**, say what happened and how to fix it, without blaming anyone.
## Showing and hiding
- Do not hide in an [accordion](/docs/components/accordion), a [carousel](/docs/components/carousel) or a tab what everyone needs: use them for what only some people want.
- On phones you can rearrange, but not remove: phone users must be able to do the same as on desktop.
## Motion
- [Animations](/docs/utilities/animations) are short and have a reason: something enters, leaves or changes.
- Everything stops if the person asks for **reduced motion** in their system (the framework already does this).
- Nothing moves on its own for more than 5 seconds without a way to stop it, like the autoplaying carousel (which has pause).
## Colors and modes
- **Light and dark with the same care**: everything is checked in both modes.
- **Never color alone** to convey information: add text or an icon (an error in coral also says it is an error).
- Text of **7:1** or more (AAA) whenever possible; the minimum is 4.5:1. Borders and icons that must be seen, 3:1.
## Consistency
- The same thing has the same name and works the same way across the site: if a button is “Save” on one page, it is not “Apply” on another.
- Use the framework components before making a new one; if a new one is needed, it is added to GlassMarshmallow so it is available in every project.