Skip to content
Palette Neutral
Download ZIP

Neutral palette · 6.7 KB ZIP File receipt View as Markdown View code

Preview

Fit to the available width. The frame follows the height of its content; previews taller than the maximum auto-height scroll inside it.

Give this component to your coding agent Copy a prompt that fetches this exact version and palette through the PageSugar MCP server.
cmp_status_badge_01 · version 1.0.0 · Neutral palette
Using the PageSugar MCP server, fetch component cmp_status_badge_01 version 1.0.0 with variant "neutral", first inspect its requirements and license status and confirm this project uses Svelte 5 and Tailwind CSS 4. Retrieve every manifest file, including binary assets and any manifest-only response files, preserving relative paths. Then integrate the source and follow its usage notes. Run project checks, review the browser result and report anything unverified. Do not substitute another version or invent missing files.

Not connected yet? Set up the MCP server

Code

Palette
Neutral
Version
1.0.0
Digest
Full digest
sha256-4ea359674ab3ab10d4d65d76407a113a2f0e6dc09e91e19b09b03e289b11cc4e

This component needs all 2 files. Download the ZIP

statuses.ts TypeScript · 1.9 KB Raw
/** The five tones a status can take. Each one has its own colour token and default shape. */
export type StatusTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';

/**
 * The indicator drawn before the label. Every name is a different silhouette, so two statuses
 * never differ by colour alone: a filled disc with a check, a clock ring, a triangle, a
 * rounded square with a cross, and a hollow ring.
 */
export type StatusIcon = 'dot' | 'check' | 'clock' | 'alert' | 'cross';

/** How one status key renders. */
export interface StatusDefinition {
	/** The visible label, such as "Operational". */
	label: string;
	/** Colour role. */
	tone: StatusTone;
	/** Indicator shape. Omitted, the tone's default shape from TONE_ICONS is used. */
	icon?: StatusIcon;
}

/** A map from the status keys in your data to how each one renders. */
export type StatusMap = Record<string, StatusDefinition>;

/** The default shape for each tone. No two tones share one. */
export const TONE_ICONS: Record<StatusTone, StatusIcon> = {
	neutral: 'dot',
	info: 'clock',
	success: 'check',
	warning: 'alert',
	danger: 'cross'
};

/**
 * Keys that cover a status page and a simple job or order lifecycle. Passing `statuses`
 * replaces this map rather than merging with it; spread it in to extend it:
 * `{ ...DEFAULT_STATUSES, refunded: { label: 'Refunded', tone: 'neutral' } }`.
 */
export const DEFAULT_STATUSES: StatusMap = {
	operational: { label: 'Operational', tone: 'success' },
	degraded: { label: 'Degraded', tone: 'warning' },
	outage: { label: 'Outage', tone: 'danger' },
	maintenance: { label: 'Maintenance', tone: 'info' },
	pending: { label: 'Pending', tone: 'info' },
	active: { label: 'Active', tone: 'success' },
	complete: { label: 'Complete', tone: 'success' },
	failed: { label: 'Failed', tone: 'danger' },
	cancelled: { label: 'Cancelled', tone: 'neutral' },
	draft: { label: 'Draft', tone: 'neutral' }
};

Pass the status key from your data, and a statuses map if the defaults (operational, degraded, outage, maintenance, pending, active, complete, failed, cancelled, draft) do not fit. It renders one static span. It does not fetch or poll status, does not decide what a status means, and is not announced when it changes unless you wrap it in a live region.

Suggested location
src/lib/components/status-badge-01
Required props
status

Limitations

  • Passing statuses replaces the default map instead of merging with it. Spread DEFAULT_STATUSES from statuses.ts into your map to keep the defaults.
  • The label never wraps. Past the width of its container it truncates with an ellipsis, so keep labels to one to three words.
  • Not a live region. If the status changes in place, wrap the badge in an element with role="status" so the change is announced.
  • The development warning for an unknown key reads import.meta.env.DEV, which Vite sets; other bundlers stay silent.
  • Light mode only. On a dark page set --status-badge-muted, --status-badge-hairline, --status-badge-surface and each tone to lighter colours, and re-check contrast.

Example

Svelte
<script lang="ts">
	import StatusBadge from '$lib/components/status-badge-01/StatusBadge.svelte';
	import type { StatusMap } from '$lib/components/status-badge-01/statuses.js';

	const orderStatuses: StatusMap = {
		paid: { label: 'Paid', tone: 'success' },
		refunded: { label: 'Refunded', tone: 'neutral' },
		disputed: { label: 'Disputed', tone: 'warning' }
	};
</script>

<StatusBadge status="operational" />
<StatusBadge status="paid" statuses={orderStatuses} prefix="Order status:" />
<StatusBadge status="degraded" indicator="dot" pulse />

The source imports its own files by relative path only. Examples import from $lib/...; SvelteKit 3 projects have no $lib alias by default, so import from your #lib subpath (with the .svelte extension) or a relative path instead.

Status badge#

A status badge shows a state that comes from your data: a service that is operational, an order that is paid, a job that failed. You pass the key; the map decides the label, the tone and the shape. It never changes on its own and never does anything when clicked.

The map is the design decision#

TypeScript
import type { StatusMap } from '$lib/components/status-badge-01/statuses.js';

const orderStatuses: StatusMap = {
	paid: { label: 'Paid', tone: 'success' },
	awaiting: { label: 'Awaiting payment', tone: 'info' },
	refunded: { label: 'Refunded', tone: 'neutral' },
	disputed: { label: 'Disputed', tone: 'warning' }
};

Keep the map in one place and reuse it everywhere the status appears, so "Disputed" is the same colour and shape on the order list, the order page and the email preview. A map you pass replaces the defaults; spread DEFAULT_STATUSES in if you want both.

Picking a tone#

  • success: done or healthy. Paid, operational, complete.
  • info: in progress or scheduled, nothing wrong. Pending, maintenance, syncing.
  • warning: needs a look. Degraded, disputed, overdue.
  • danger: broken or blocked. Failed, outage.
  • neutral: finished without a verdict, or not started. Draft, cancelled, refunded.

Each tone has its own shape (a check disc, a clock, a triangle, a square, a ring), so a column of statuses still reads for someone who cannot tell green from red. Override icon per entry only when the default shape says the wrong thing.

Unknown keys#

A key that is not in the map renders neutral, labelled with fallbackLabel or the key itself, and logs a warning in development. It never renders blank, so a new backend state shows up as something you can see and fix.

Updating in place#

The badge is not a live region. If it changes while the page is open (a deploy finishing, a payment clearing), wrap it:

Svelte
<span role="status"><StatusBadge {status} /></span>

Props and content inputs#

On this page
NameTypeRequiredDefaultDescription
statusstringYesNoneThe status key from your data, such as operational or paid.
statusesRecord<string, { label: string; tone: 'neutral' | 'info' | 'success' | 'warning' | 'danger'; icon?: 'dot' | 'check' | 'clock' | 'alert' | 'cross' }>NoDEFAULT_STATUSESMap from key to label, tone and optional icon. Replaces the defaults; spread DEFAULT_STATUSES in to extend them. Without icon, each tone uses its own shape.
indicator'icon' | 'dot'No'icon'A 14 px icon, or a compact 10 px mark. Both keep a different shape per tone.
prefixstringNoNoneVisually hidden text read before the label, such as 'Order status:', for tables where the column header is not read.
pulsebooleanNofalsePulse a ring out from the indicator for a live state. No animation under prefers-reduced-motion.
fallbackLabelstringNoNoneLabel for a key the map does not have. Defaults to the key itself; an empty key reads Unknown.
classstringNoNoneExtra classes on the badge, for margins or a tighter max-width.

Customization#

On this page

Supply your own statuses map for labels, tones and icons. Retone with --status-badge-* variables: one per tone, and each tint and ring follows. Edit TONE_ICONS in statuses.ts to change which shape a tone uses.

  • Your own states: pass statuses={{ shipped: { label: 'Shipped', tone: 'success' }, on_hold: { label: 'On hold', tone: 'warning', icon: 'clock' } }}. Keys match the values in your data exactly.
  • Keep the defaults and add to them: statuses={{ ...DEFAULT_STATUSES, archived: { label: 'Archived', tone: 'neutral' } }}, importing DEFAULT_STATUSES from statuses.ts.
  • Brand-aligned tones: set --status-badge-success, --status-badge-warning, --status-badge-danger and --status-badge-info on the page or an ancestor. Each label has to clear 4.5:1 against its own 8% tint, so pick a step that clears about 5:1 on white.
  • Neutral tone: --status-badge-muted is its label, ring mark and tint, and --status-badge-hairline its outline.
  • Dark or tinted page: set --status-badge-muted to zinc-400, --status-badge-hairline to rgb(255 255 255 / 0.12), --status-badge-surface to the page colour, and each tone to its 300 or 400 step.
  • Shape: the badge is a 6 px rounded rectangle. For a pill, change rounded-md to rounded-full on the root.
  • In a table: pass prefix="Order status:" so a screen reader moving cell by cell hears what the value is. Use indicator="dot" in dense rows.
  • Live updates: wrap the badge in <span role="status"> when the value changes while the page is open.

Public CSS variables

VariableToken
--status-badge-mutedmuted
--status-badge-hairlinehairline
--status-badge-surfacesurface
--status-badge-infoinfo
--status-badge-successsuccess
--status-badge-warningwarning
--status-badge-dangerdanger

Accessibility#

On this page
  • Renders a plain span with no role and no live region: a status in a table or panel is read in place, not announced. Wrap it in role="status" yourself if the value updates while the page is open.
  • The label carries the meaning (WCAG 1.4.1). Each tone also has a distinct shape, so the state reads without colour, but the shape is aria-hidden and never the only signal.
  • Use prefix in tables and lists where the column header is not read with the cell, so the badge reads as 'Order status: Paid' rather than 'Paid'.
  • Every default tone's label clears 4.5:1 against its tint on white, zinc-50 and zinc-100 pages. Re-check if you set a tone variable.
  • The pulse never runs under prefers-reduced-motion; the indicator then stands still.
  • A truncated label is cut visually only; the full text stays in the DOM and is read in full.
  • Not focusable and not interactive.

Known limitations

  • Contrast is checked for the default colours on light pages only; retoned or dark pages are the consumer's to check.

Release details#

On this page
Integration
  • Presentational
  • Works without client-side JavaScript
  • Server-side rendering supported
Dependencies
No additional runtime packages beyond Svelte and Tailwind CSS
License

MIT. Default license approval is pending; see the license status before adopting the source.

Version history
  • 1.0.0 (Published) Current release · 21 October 2026

Only the current release is available. Keep downloaded source and its receipt if you need to use it again later.