What a program prints as it runs: wrapped lines in a well that keeps the newest in view until the reader scrolls up.
› npm run build vite v8.0.2 building for production… ✓ 412 modules transformed. dist/index.html 0.62 kB dist/assets/index-4f1c2a.css 38.40 kB dist/assets/index-9b7e11.js 184.12 kB
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
statesmbk-log--state-<value> | runningfinished | running | Running while the program prints, finished once it stops. The badge says which, and a screen reader hears when it starts and when it ends. |
Examples
Usage
A running log: its title, a Running badge, a note of what was left out and the newest line in view.
40 earlier lines left out
› npm run build vite v8.0.2 building for production… ✓ 412 modules transformed. dist/index.html 0.62 kB dist/assets/index-4f1c2a.css 38.40 kB dist/assets/index-9b7e11.js 184.12 kB
Live output
Press Run the build: lines arrive while the newest stays in view, until you scroll up. A screen reader hears the start and the end, not the lines.
Waiting for output…
Waiting
Nothing printed yet: the badge says Waiting and the well says so too.
Waiting for output…
Finished
The badge turns to Finished, and a screen reader hears that the output ended.
40 earlier lines left out
› npm run build vite v8.0.2 building for production… ✓ 412 modules transformed. dist/index.html 0.62 kB dist/assets/index-4f1c2a.css 38.40 kB dist/assets/index-9b7e11.js 184.12 kB ✓ built in 4.81s
Scrolled up
The reader scrolled up, so the log stopped following; "Jump to latest" brings the newest line back and follows again.
› npm run upload uploaded pages/guide-01.html (1.2 kB) uploaded pages/guide-02.html (1.6 kB) uploaded pages/guide-03.html (2.0 kB) uploaded pages/guide-04.html (2.4 kB) uploaded pages/guide-05.html (2.8 kB) uploaded pages/guide-06.html (3.2 kB) uploaded pages/guide-07.html (3.6 kB) uploaded pages/guide-08.html (1.2 kB) uploaded pages/guide-09.html (1.6 kB) uploaded pages/guide-10.html (2.0 kB) uploaded pages/guide-11.html (2.4 kB) uploaded pages/guide-12.html (2.8 kB) uploaded pages/guide-13.html (3.2 kB) uploaded pages/guide-14.html (3.6 kB) uploaded pages/guide-15.html (1.2 kB) uploaded pages/guide-16.html (1.6 kB) uploaded pages/guide-17.html (2.0 kB) uploaded pages/guide-18.html (2.4 kB) uploaded pages/guide-19.html (2.8 kB) uploaded pages/guide-20.html (3.2 kB) uploaded pages/guide-21.html (3.6 kB) uploaded pages/guide-22.html (1.2 kB) uploaded pages/guide-23.html (1.6 kB) uploaded pages/guide-24.html (2.0 kB) uploaded pages/guide-25.html (2.4 kB) uploaded pages/guide-26.html (2.8 kB) uploaded pages/guide-27.html (3.2 kB) uploaded pages/guide-28.html (3.6 kB) uploaded pages/guide-29.html (1.2 kB) uploaded pages/guide-30.html (1.6 kB) uploaded pages/guide-31.html (2.0 kB) uploaded pages/guide-32.html (2.4 kB) uploaded pages/guide-33.html (2.8 kB) uploaded pages/guide-34.html (3.2 kB) uploaded pages/guide-35.html (3.6 kB) uploaded pages/guide-36.html (1.2 kB)
Long lines
Lines wrap at the well's width, so nothing scrolls sideways, on a phone too.
› npm ci npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value. added 812 packages in 9s
Guidelines
Purpose
Shows what a program prints as it runs, such as a build, an upload or a job, and keeps it after the program finishes. The newest line stays in view while it prints, unless the reader scrolls up to read.
When to use
- For the output of a build, an upload, a test run or a script, while it runs and after.
- For any text that arrives line by line and is read as it comes.
When not to use
- For code a reader copies or studies. Use a code block.
- For messages a reader must act on. Use an alert or a toast.
- For a list of past events with times and people. Use a timeline.
Usage rules
Do
- Name the log for what printed it.
- Say in the note what was left out when you drop old lines.
- Set
stateto finished when the program stops.
Don't
- Announce each line.
- Color lines to mean errors with no words.
- Use a log for code to copy.
120 earlier lines left out
dist/index.html 0.62 kB dist/assets/index-4f1c2a.css 38.40 kB dist/assets/index-9b7e11.js 184.12 kB
Say what was left out when a long output is cut, so the reader knows the start is missing.
dist/index.html 0.62 kB dist/assets/index-4f1c2a.css 38.40 kB dist/assets/index-9b7e11.js 184.12 kB
Cutting lines without a note makes the output look as if it starts in the middle.
Anatomy
- The block: the header, then the well.
- The header part: the title part in a medium label style, then the status part, a small soft badge that says Waiting, Running or Finished.
- The well part, the sunken surface inside a subtle hairline with the small container corners, no taller than a long list.
- The scroll part, the area inside the well that scrolls.
- The note part, an optional line in a small body style and the subtle text color, such as "40 earlier lines left out", with the divider part under it.
- The lines part, the output in the code style, left to right, wrapping at the well's width. Before anything is printed it says "Waiting for output…" in the subtle text color.
- The jump part, a small ghost "Jump to latest" button at the well's foot, there only while the reader is scrolled up.
- The announcement part, a status line a screen reader hears, in the page but not drawn.
Behavior
Set state to running while the program prints and to finished once it stops; the badge follows, in the info color while running and the success color when finished. With no lines yet it says Waiting.
While running, the well keeps the newest line in view as lines arrive and as the lines rewrap. When the reader scrolls up, the log stops following, so the lines they are reading stay still, and "Jump to latest" appears at the well's foot. Pressing it goes to the newest line at once, follows again and puts the keyboard back in the well; scrolling back to the end does the same. A page can also hold the following itself with following and onFollowingChange.
Lines wrap at any point, so nothing scrolls sideways, on a phone too. Keep the lines you pass to a few hundred; for more, drop the oldest and say so in the note.
Accessibility
What it does for you
- The well is one stop for the Tab key and a region named by the title (or by
aria-labelwhen the log has no title), so the arrow keys scroll it. It shows the focus ring inside its edge, drawn in the system's highlight color in a forced-color mode. - The lines are never read out one by one: hundreds of lines would drown everything else. A screen reader hears when the log starts printing ("Build output: Running") and when it finishes ("Build output: Finished").
statusadds the page's own short status, such as "120 lines printed", heard when it changes.- Every word it says by itself, such as the state on its badge and the jump button, keeps its English unless the page gives its own.
What you must do
- Name the log for what printed it, with a title or an
aria-label. Without a name the wrapper warns in development. - Change
statusseldom: every few seconds at most. - Don't say an error by the color of a line alone; say it in words in the output, or in an alert beside the log.
- Give the state's words and the jump button's label in the page's own language when the page is not in English.
How to test
- With the keyboard, Tab reaches the log's well once, with the focus ring inside its edge. The arrow keys scroll it, and Home and End jump to the first and last lines.
- Scroll up while it runs: the log stops following, and "Jump to latest" appears at the well's foot. Press it with Enter or Space: the newest line shows and the keyboard is back in the well.
- With a screen reader, the log is read as a region by its title, and it announces "Running" and "Finished" but not each line.
- With text at 200% and the page 320 pixels wide, the lines wrap and nothing scrolls sideways.
- In a forced-color mode, the focus ring around the well is visible.
Keyboard
| Key | What it does |
|---|---|
| Tab | Moves focus to the well, which is one stop. |
| Down ArroworUp Arrow | Scrolls the lines. |
| HomeorEnd | Scrolls to the first or last line. |
| EnterorSpace | On "Jump to latest", goes to the newest line, follows again and puts the keyboard back in the well. |
Source
The files the log is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-log — Log. Figma "Log" 7118:1670 (State: Empty, Running, Finished, Scrolled up; Header boolean).
* What a program prints as it runs: a header with the log's title in Label MD and a small soft badge that
* says Waiting, Running or Finished; then the well, the sunken surface inside a subtle hairline with the
* small container corners, holding an optional note ("40 earlier lines left out") over a divider and the
* lines in Code MD, which wrap so nothing scrolls sideways. The well is no taller than a long list and
* scrolls inside itself; while the reader is scrolled up, "Jump to latest" waits at its foot. */
.smbk-log {
box-sizing: border-box;
/* Holds the status line, which is out of sight, inside the log rather than wherever the page would put it. */
position: relative;
display: flex;
flex-direction: column;
gap: var(--smbk-space-stack-sm);
width: 100%;
color: var(--smbk-color-text-default);
}
.smbk-log__header {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--smbk-space-inline-sm);
}
.smbk-log__title {
color: var(--smbk-color-text-default);
font-family: var(--smbk-typography-label-md-font-family);
font-size: var(--smbk-typography-label-md-font-size);
font-weight: var(--smbk-typography-label-md-font-weight);
line-height: var(--smbk-typography-label-md-line-height);
letter-spacing: var(--smbk-typography-label-md-letter-spacing);
text-transform: var(--smbk-typography-label-md-text-transform);
}
.smbk-log__well {
box-sizing: border-box;
display: flex;
flex-direction: column;
max-height: var(--smbk-size-list-max-height);
overflow: hidden;
border: var(--smbk-border-width-hairline) solid var(--smbk-color-border-subtle);
border-radius: var(--smbk-radius-container-sm);
background: var(--smbk-color-surface-sunken);
}
/* The well's ring when its scrolling area has the keyboard, drawn inside its edge. */
.smbk-log__well:has(.smbk-log__scroll:focus-visible) {
outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
outline-offset: calc(-1 * var(--smbk-focus-ring-width));
}
.smbk-log__scroll {
box-sizing: border-box;
display: flex;
flex: 1 1 auto;
flex-direction: column;
gap: var(--smbk-space-stack-sm);
min-height: 0;
padding: var(--smbk-space-inset-lg);
overflow-x: hidden;
overflow-y: auto;
outline: none;
/* The system's scrollbar: thin, in the subtle border color, on a clear track. */
scrollbar-width: thin;
scrollbar-color: var(--smbk-color-border-subtle) transparent;
}
/* Figures of one width, so a count that grows as the program prints ("40 earlier lines left out") never moves the
words after it. */
.smbk-log__note {
margin: 0;
color: var(--smbk-color-text-subtle);
font-family: var(--smbk-typography-body-sm-font-family);
font-size: var(--smbk-typography-body-sm-font-size);
font-weight: var(--smbk-typography-body-sm-font-weight);
line-height: var(--smbk-typography-body-sm-line-height);
letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
font-variant-numeric: tabular-nums;
}
.smbk-log__divider {
flex-shrink: 0;
}
/* The lines: the code style, always left to right, wrapping at any point so a long line never runs past
the well. */
.smbk-log__lines {
margin: 0;
color: var(--smbk-color-text-default);
font-family: var(--smbk-typography-code-md-font-family);
font-size: var(--smbk-typography-code-md-font-size);
font-weight: var(--smbk-typography-code-md-font-weight);
line-height: var(--smbk-typography-code-md-line-height);
letter-spacing: var(--smbk-typography-code-md-letter-spacing);
text-transform: var(--smbk-typography-code-md-text-transform);
white-space: pre-wrap;
overflow-wrap: anywhere;
direction: ltr;
text-align: left;
}
/* Nothing printed yet: the waiting words in the subtle text color. */
.smbk-log__lines--empty {
color: var(--smbk-color-text-subtle);
}
.smbk-log__jump {
display: flex;
flex-shrink: 0;
justify-content: flex-end;
padding: 0 var(--smbk-space-inset-lg) var(--smbk-space-inset-lg);
}
/* What changed, for a screen reader: in the page, not drawn. */
/* .smbk-log__announcement: hidden from view, kept for screen readers and the keyboard, by the shared rule in visually-hidden/visually-hidden.css. */
.smbk-log__announcement {
clip-path: inset(50%);
}
/* ---- high contrast (forced colors) ---- */
@media (forced-colors: active) {
.smbk-log__well:has(.smbk-log__scroll:focus-visible) {
outline-color: Highlight;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| space.stack.sm--smbk-space-stack-sm | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| color.text.default--smbk-color-text-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| space.inline.sm--smbk-space-inline-sm | comfortable: 6pxcompact: 4pxchanges with density | Spacing | |
| typography.label-md--smbk-typography-label-md-font-family--smbk-typography-label-md-font-size--smbk-typography-label-md-font-weight--smbk-typography-label-md-line-height--smbk-typography-label-md-letter-spacing--smbk-typography-label-md-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| size.list.max-height--smbk-size-list-max-height | 320px | Size | |
| border-width.hairline--smbk-border-width-hairline | 1px | Border width | |
| color.border.subtle--smbk-color-border-subtle | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| radius.container.sm--smbk-radius-container-sm | 8px | Radius | |
| color.surface.sunken--smbk-color-surface-sunken | light: #373d430ddark: #00000066changes with theme | Color | |
| focus.ring.width--smbk-focus-ring-width | 2px | Focus | |
| color.border.focus--smbk-color-border-focus | light: #cc0d0ddark: #f34336changes with theme | Color | |
| space.inset.lg--smbk-space-inset-lg | comfortable: 12pxcompact: 8pxchanges with density | Spacing | |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| typography.code-md--smbk-typography-code-md-font-family--smbk-typography-code-md-font-size--smbk-typography-code-md-font-weight--smbk-typography-code-md-line-height--smbk-typography-code-md-letter-spacing--smbk-typography-code-md-text-transform | fontFamily: Geist Mono, Geist Mono Fallback, ui-monospace, monospacefontSize: 14pxfontWeight: 400lineHeight: 1.6letterSpacing: 0px | The quick brown fox | Typography |
Manifest
{
"name": "log",
"block": "smbk-log",
"summary": "What a program prints as it runs: wrapped lines in a well that keeps the newest in view until the reader scrolls up.",
"adapt": "css",
"axes": {
"state": {
"values": [
"running",
"finished"
],
"default": "running",
"description": "Running while the program prints, finished once it stops. The badge says which, and a screen reader hears when it starts and when it ends."
}
},
"flags": {},
"parts": [
"header",
"title",
"status",
"well",
"scroll",
"note",
"divider",
"lines",
"jump",
"announcement"
],
"figmaNodeId": "7118:1670"
}