Add Info, Tip, Warning, Danger and Note callouts to the docs (#2916)

This commit is contained in:
Paweł Kuna authored and GitHub committed 2026-08-31 18:30:08 +02:00
1 parent f35aab3ab4
commit 135f38d4b9
15 files changed
+271 -5

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@tabler/docs": minor
---
Added `Info`, `Tip`, `Warning`, `Danger` and `Note` callouts, usable in any MDX page, with a reference page under Resources.
+40
View File
@@ -0,0 +1,40 @@
---
// A tinted aside for a note, a tip or a warning inside docs prose. MDX pages use the
// named wrappers next to this file (<Info>, <Tip>, <Warning>, <Danger>, <Note>), which
// [...slug].astro passes to <Content /> — so a page can call them without importing.
import Icon from '@ui/Icon.astro'
export type CalloutType = 'info' | 'tip' | 'warning' | 'danger' | 'note'
interface Props {
type?: CalloutType
/** Bold first line. Without it the callout is body text only. */
title?: string | undefined
}
const { type = 'info', title } = Astro.props
const icons: Record<CalloutType, string> = {
info: 'info-circle',
tip: 'bulb',
warning: 'alert-triangle',
danger: 'alert-circle',
note: 'pencil',
}
const labels: Record<CalloutType, string> = {
info: 'Info',
tip: 'Tip',
warning: 'Warning',
danger: 'Danger',
note: 'Note',
}
---
<aside class:list={['callout', `callout-${type}`]} role="note" aria-label={title ? `${labels[type]}: ${title}` : labels[type]}>
<Icon name={icons[type]} class="callout-icon" />
<div class="callout-body">
{title && <p class="callout-title">{title}</p>}
<slot />
</div>
</aside>
+11
View File
@@ -0,0 +1,11 @@
---
import Callout from '../Callout.astro'
interface Props {
title?: string | undefined
}
---
<Callout type="danger" title={Astro.props.title}>
<slot />
</Callout>
+11
View File
@@ -0,0 +1,11 @@
---
import Callout from '../Callout.astro'
interface Props {
title?: string | undefined
}
---
<Callout type="info" title={Astro.props.title}>
<slot />
</Callout>
+11
View File
@@ -0,0 +1,11 @@
---
import Callout from '../Callout.astro'
interface Props {
title?: string | undefined
}
---
<Callout type="note" title={Astro.props.title}>
<slot />
</Callout>
+11
View File
@@ -0,0 +1,11 @@
---
import Callout from '../Callout.astro'
interface Props {
title?: string | undefined
}
---
<Callout type="tip" title={Astro.props.title}>
<slot />
</Callout>
+11
View File
@@ -0,0 +1,11 @@
---
import Callout from '../Callout.astro'
interface Props {
title?: string | undefined
}
---
<Callout type="warning" title={Astro.props.title}>
<slot />
</Callout>
+3 -1
View File
@@ -37,7 +37,9 @@ Then create a free account on [mapbox.com](https://www.mapbox.com/) and copy you
mapboxgl.accessToken = 'pk.your-token-here' mapboxgl.accessToken = 'pk.your-token-here'
``` ```
Use a public token only. A secret token (`sk.`) must never be sent to the browser. Limit the token to your own domains in the Mapbox dashboard. <Danger title="Use a public token only">
A secret token (`sk.`) must never be sent to the browser. Limit the token to your own domains in the Mapbox dashboard.
</Danger>
## Usage ## Usage
@@ -0,0 +1,88 @@
---
title: Docs callouts
summary: Callouts pull one sentence out of the flow of a page - a prerequisite, a gotcha, a warning. Tabler docs ship five of them, and any MDX page can use them without an import.
description: Info, Tip, Warning, Danger and Note callouts for Tabler documentation pages, with usage rules and accessibility notes.
---
## Overview
A callout is a short aside inside a docs page. It carries something a reader must not miss while scanning: a step to do first, a mistake that costs an hour, a limit of the library.
Callouts belong to the documentation, not to the Tabler UI package. For an alert inside a product interface, use the [alert component](/ui/components/alert) instead.
## Variants
Five variants cover what a docs page needs. The icon and the border carry the state color; the text stays body color, so a callout never depends on color alone.
### Info
Use it for context and prerequisites - something to know or to do before the rest of the page makes sense.
<Info title="Read the installation guide first">
The examples on this page assume Tabler is already installed and the stylesheet is on the page.
</Info>
```mdx
<Info title="Read the installation guide first">
The examples on this page assume Tabler is already installed.
</Info>
```
### Tip
Use it for advice that makes the task easier, but is not required to finish it.
<Tip title="Set the theme before init">
Some plugins keep the skin they were created with, so switching the theme afterwards has no effect.
</Tip>
### Warning
Use it when missing the information breaks something later - a deprecation, a load order, a license limit.
<Warning title="`@import` is on its way out">
It still compiles, but Dart Sass marks it as deprecated and will remove it in Sass 3.0.
</Warning>
### Danger
Use it for what cannot be undone or what is unsafe - lost data, leaked credentials.
<Danger title="Use a public token only">
A secret token must never be sent to the browser. Limit the token to your own domains.
</Danger>
### Note
A callout with no title is body text only. Use it for a short aside that needs no heading.
<Note>
Tabler ships the compiled CSS in `dist/css`, so a project without a Sass pipeline can still use it.
</Note>
## Usage
Every MDX page in the docs can use the five tags without importing them - `docs/pages/[...slug].astro` passes them to the rendered content.
```mdx
<Warning title="Optional title">
Body text, with **markdown** and [links](/ui/getting-started/installation).
</Warning>
```
The `title` prop is optional and renders as the first line in medium weight. The body is markdown, so keep it on its own lines inside the tags.
## When to use one
A callout works because it is rare. On a page where every third paragraph is boxed, nothing stands out and the reader skips all of them.
- Use one when missing the information causes a real problem: a broken build, a security hole, a step done in the wrong order.
- Keep it to one or two per page.
- Do not box the main instruction of a section. If the paragraph is what the section is about, it belongs in the flow, under its heading.
- Do not box a list of accessibility rules or best practices. Those are lists, and they read better as lists.
## Accessibility
- Each callout renders as `<aside role="note">` with an `aria-label` built from the variant and the title, so a screen reader announces "Warning: `@import` is on its way out" instead of an unlabeled group.
- The state color sits on the icon and the border. The text keeps the body color, which passes contrast in both color modes and does not carry meaning through color alone (WCAG 1.4.1).
- The icon is decorative and hidden from assistive tech - the variant is already in the label.
+3 -1
View File
@@ -128,7 +128,9 @@ The Sass sources now use the module system (`@use` and `@forward`). Setting vari
+ ); + );
``` ```
`@import` still compiles, but Dart Sass marks it as deprecated and will remove it in Sass 3.0. Move to `@use` now. <Warning title="`@import` is on its way out">
It still compiles, but Dart Sass marks it as deprecated and will remove it in Sass 3.0. Move to `@use` now.
</Warning>
### Custom property prefix moved to PostCSS ### Custom property prefix moved to PostCSS
+3 -1
View File
@@ -7,7 +7,9 @@ related: [/ui/layout/page-headers, /ui/layout/navbars]
import Example from '@components/Example.astro' import Example from '@components/Example.astro'
Before you start with this section, make sure you have followed the [installation guideline](/ui/getting-started/installation). <Info>
Before you start with this section, make sure you have followed the [installation guideline](/ui/getting-started/installation).
</Info>
## Sample layout ## Sample layout
+3 -1
View File
@@ -73,7 +73,9 @@ if (document.documentElement.getAttribute('data-bs-theme') === 'dark') {
hugeRTE.init(options); hugeRTE.init(options);
``` ```
The editor keeps that skin until it is recreated, so switch the theme before initializing, or destroy and init again when the user changes it. <Tip title="Set the theme before init">
The editor keeps that skin until it is recreated, so switch the theme before initializing, or destroy and init again when the user changes it.
</Tip>
## Content styles ## Content styles
+9 -1
View File
@@ -7,6 +7,14 @@ import { getCollection, render } from 'astro:content'
import DocsLayout from '@layouts/DocsLayout.astro' import DocsLayout from '@layouts/DocsLayout.astro'
import { docsUrlFromId } from '@lib/docs-pages' import { docsUrlFromId } from '@lib/docs-pages'
import { markdownUrlFromDocsUrl } from '@lib/markdown-url' import { markdownUrlFromDocsUrl } from '@lib/markdown-url'
import Danger from '@components/callouts/Danger.astro'
import Info from '@components/callouts/Info.astro'
import Note from '@components/callouts/Note.astro'
import Tip from '@components/callouts/Tip.astro'
import Warning from '@components/callouts/Warning.astro'
// Callouts are available to every MDX page without an import of its own.
const callouts = { Danger, Info, Note, Tip, Warning }
export const getStaticPaths = (async () => { export const getStaticPaths = (async () => {
const entries = await getCollection('docs') const entries = await getCollection('docs')
@@ -49,5 +57,5 @@ if (data.classnames) {
markdownUrl={markdownUrlFromDocsUrl(docsUrlFromId(entry.id))} markdownUrl={markdownUrlFromDocsUrl(docsUrlFromId(entry.id))}
editPath={entry.filePath ? `docs/${entry.filePath}` : undefined} editPath={entry.filePath ? `docs/${entry.filePath}` : undefined}
> >
<Content /> <Content components={callouts} />
</DocsLayout> </DocsLayout>
+58
View File
@@ -522,3 +522,61 @@ header > .navbar {
border-start-start-radius: 0; border-start-start-radius: 0;
border-start-end-radius: 0; border-start-end-radius: 0;
} }
// -----------------------------------------------------------------------------
// Callouts: a tinted aside for a note, a tip or a warning inside prose. The icon
// carries the state color while the text stays body color, so the block reads as
// part of the page — and so a pale state like yellow never has to carry text.
// -----------------------------------------------------------------------------
.callout {
display: flex;
gap: 0.75rem;
padding: 1rem 1.25rem;
margin-block: 1.5rem;
font-size: 0.875rem;
line-height: 1.5;
color: var(--tblr-body-color);
background: var(--tblr-bg-surface-secondary);
border: 1px solid var(--tblr-border-color);
border-radius: var(--tblr-border-radius-lg);
}
.callout-icon {
flex: none;
margin-top: 0.0625rem;
color: var(--tblr-secondary);
}
.callout-title {
margin-bottom: 0.25rem;
font-weight: var(--tblr-font-weight-medium);
}
// The slot holds markdown, so the trailing paragraph or list brings its own
// bottom margin — which would pad the callout unevenly.
.callout-body > :last-child {
margin-bottom: 0;
}
// The prose link treatment targets direct children of .prose, so it stops at the
// callout. Links inside one get the same quiet underline (WCAG 1.4.1).
.callout a:not(.btn) {
text-decoration: underline;
text-decoration-color: color-mix(in sRGB, var(--tblr-primary) 40%, transparent);
text-underline-offset: 3px;
&:hover {
text-decoration-color: var(--tblr-primary);
}
}
@each $state, $color in ('info': 'azure', 'tip': 'green', 'warning': 'yellow', 'danger': 'red') {
.callout-#{$state} {
background: var(--tblr-#{$color}-lt);
border-color: color-mix(in srgb, var(--tblr-#{$color}) 24%, transparent);
.callout-icon {
color: var(--tblr-#{$color});
}
}
}
+4
View File
@@ -487,6 +487,10 @@
"title": "How to contribute", "title": "How to contribute",
"url": "/ui/getting-started/how-to-contribute" "url": "/ui/getting-started/how-to-contribute"
}, },
{
"title": "Docs callouts",
"url": "/ui/getting-started/callouts"
},
{ {
"title": "Tabler license", "title": "Tabler license",
"url": "/ui/getting-started/license" "url": "/ui/getting-started/license"