Skip to content
Palette Inherited colour
Download ZIP

Inherited colour palette · 4.4 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_icon_01 · version 1.0.0 · Inherited colour palette
Using the PageSugar MCP server, fetch component cmp_icon_01 version 1.0.0 with variant "default", 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
Inherited colour
Version
1.0.0
Digest
Full digest
sha256-0e26d41df6f16cf6ee221800fc4909ea3f42fd70b0b567fc015a5973894c7aea
Icon.svelte Svelte · 3.3 KB Raw
<script lang="ts" module>
	export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
</script>

<script lang="ts">
	import type { Snippet } from 'svelte';

	interface Props {
		/**
		 * Accessible name, and it is required. null marks the icon decorative (hidden from
		 * assistive technology); a string makes it an image with that name.
		 */
		label: string | null;
		/** Path data for a single-path icon: the d attribute. */
		path?: string;
		/** SVG child elements for a multi-shape icon. Used when path is absent. */
		children?: Snippet;
		/** The icon's drawing grid. */
		viewBox?: string;
		/** A fixed square step. Omitted, the icon is 1em and follows the text around it. */
		size?: IconSize;
		/** Draw with a round-capped stroke and no fill, for outline icon sets. */
		strokeMode?: boolean;
		/** Mirror in right-to-left text, for directional icons such as arrows and chevrons. */
		mirror?: boolean;
		/** Extra classes on the svg: colour, margins. */
		class?: string;
	}

	let {
		label,
		path,
		children,
		viewBox = '0 0 24 24',
		size,
		strokeMode = false,
		mirror = false,
		class: className
	}: Props = $props();

	/*
	 * Fixed steps in pixels, written as width and height attributes rather than size classes so a
	 * size-* class passed in through class always wins.
	 */
	const PIXELS: Record<IconSize, number> = { xs: 12, sm: 16, md: 20, lg: 24, xl: 32 };

	/*
	 * Stroke weight on screen at each step. 16 px icons sit beside 13 and 14 px labels at weight
	 * 500, so they draw heavier relative to their size than the 20 and 24 px steps do. At 12 px a
	 * heavier line closes the counters, so xs steps back to 1.25.
	 */
	const STROKE: Record<IconSize, number> = { xs: 1.25, sm: 1.75, md: 1.5, lg: 1.5, xl: 2 };

	const step = $derived(size && size in PIXELS ? size : undefined);
	const name = $derived(typeof label === 'string' && label.trim() ? label.trim() : null);

	/*
	 * Stroke width is in viewBox units. A square viewport fits the grid by its longer side, so
	 * convert the on-screen weight through that side. A malformed viewBox falls back to 24.
	 */
	const grid = $derived.by(() => {
		const [, , w, h] = viewBox
			.trim()
			.split(/[\s,]+/)
			.map(Number);
		const side = Math.max(w, h);
		return Number.isFinite(side) && side > 0 ? side : 24;
	});
	const strokeWidth = $derived(
		+(step ? (STROKE[step] * grid) / PIXELS[step] : grid / 12).toPrecision(4)
	);
	const dimension = $derived(step ? PIXELS[step] : '1em');
</script>

<svg
	xmlns="http://www.w3.org/2000/svg"
	{viewBox}
	width={dimension}
	height={dimension}
	fill={strokeMode ? 'none' : 'currentColor'}
	stroke={strokeMode ? 'currentColor' : undefined}
	stroke-width={strokeMode ? strokeWidth : undefined}
	stroke-linecap={strokeMode ? 'round' : undefined}
	stroke-linejoin={strokeMode ? 'round' : undefined}
	role={name ? 'img' : undefined}
	aria-label={name ?? undefined}
	aria-hidden={name ? undefined : 'true'}
	focusable="false"
	class={[
		'inline-block shrink-0',
		// 1em icons drop by an eighth of the text so they centre on the capitals, not the baseline.
		step ? 'align-middle' : 'align-[-0.125em]',
		// :dir() reads the resolved direction, so an ltr island inside an rtl page is not flipped.
		mirror && '[&:dir(rtl)]:-scale-x-100',
		className
	]}
>
	{#if path}
		<path d={path} />
	{:else if children}
		{@render children()}
	{/if}
</svg>

Pass label (a name, or null for decorative) and either path data or child shapes. It renders one inline svg in currentColor at 1em or a fixed step. It does not ship an icon set, fetch SVG files, accept raw SVG strings, add tooltips or make the icon focusable.

Suggested location
src/lib/components/icon-01
Required props
label

Limitations

  • No icons are included. Paste path data from your own drawings or a set whose licence allows it, or install an icon package and pass its shapes as children.
  • Path data is a single d attribute. Icons that need several elements, a fill-rule or mixed fill and stroke go in children instead.
  • A filled icon with holes must draw its counters in the opposite direction, or pass fill-rule="evenodd" on a child path, because the svg uses the default nonzero rule.
  • The fixed steps set an on-screen stroke weight and convert it through the viewBox's longer side, so sets drawn on 16 or 20 unit grids keep the same weight. Their own optical sizes are not chosen for you.
  • Mirroring flips the drawing horizontally under dir="rtl"; icons whose meaning does not depend on direction (a check, a clock) should not pass mirror.

Example

Svelte
<script lang="ts">
	import Icon from '$lib/components/icon-01/Icon.svelte';

	const check = 'M5 12.5l4.5 4.5L19 7.5';
</script>

<p>
	<Icon label={null} path={check} strokeMode /> Guests can comment without a seat
</p>

<button type="button" class="inline-flex items-center gap-2">
	<Icon label={null} size="sm" strokeMode>
		<path d="M10 11.5a3.75 3.75 0 1 0 0-7.5 3.75 3.75 0 0 0 0 7.5Z" />
		<path d="M3.5 20a6.5 6.5 0 0 1 13 0M19 8v6M16 11h6" />
	</Icon>
	Invite a guest
</button>

<Icon label="Overdue" size="sm" path="M12 3 22 20H2Z" class="text-amber-700" />

Icon#

label is required, and it is the whole point: every icon on the page has made its accessibility decision in the markup. null hides the icon from assistive technology; a string names it.

Svelte
<script lang="ts">
	import Icon from '$lib/components/icon-01/Icon.svelte';

	const clock = 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18ZM12 7.5V12l3 2';
	const warning = 'M12 3 22 20H2Z';
</script>

<!-- Decorative: the text beside it says the same thing. -->
<p class="text-sm text-zinc-600">
	<Icon label={null} path={clock} strokeMode /> Due Thursday 9 October
</p>

<!-- Meaningful: nothing else says "Overdue". -->
<Icon label="Overdue" size="sm" path={warning} class="text-amber-700" />

Which one to choose#

The icon… label Renders
sits beside text that says the same thing null aria-hidden="true", focusable="false"
is the only child of a button or link null the same; name the button instead
carries meaning nothing else on the page does "Overdue" role="img", aria-label="Overdue"

An icon-only button names the button, not the icon:

Svelte
<button type="button" aria-label="Close" class="grid size-11 place-items-center rounded-lg">
	<Icon label={null} size="md" path="M6 6l12 12M18 6 6 18" strokeMode />
</button>

Sizes#

size Pixels Stroke weight Use
(omitted) 1em 1/12 of grid inside text: paragraphs, headings
xs 12 1.25 px captions, badges
sm 16 1.75 px buttons, dense rows, beside 13–14 px
md 20 1.5 px navigation, lists
lg 24 1.5 px illustrations, empty states
xl 32 2 px large illustrations

Stroke weights are on-screen pixels, converted through the longer side of the viewBox, so a set drawn on a 20 or 16 unit grid gets the same weight when it passes its own viewBox. A 1em icon drops by 0.125em so it centres on the capitals of the text around it rather than sitting on the baseline.

Beside text that wraps#

Centre the icon on the first line, not on the paragraph. Put it in a box exactly as tall as one line of the text beside it (h-6 for text-base/6):

Svelte
<p class="flex gap-3 text-base/6">
	<span class="flex h-6 items-center text-lg">
		<Icon label={null} path="M5 12.5l4.5 4.5L19 7.5" strokeMode />
	</span>
	<span>Guests can comment on milestones and follow the timeline without taking a paid seat.</span>
</p>

Several shapes#

Pass children when an icon needs more than one element, a fill-rule, or a filled part inside an outline icon:

Svelte
<Icon label={null} strokeMode>
	<path d="M6 5h12a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2ZM4 10h16" />
	<path d="M8 13h3v3H8Z" fill="currentColor" stroke="none" />
</Icon>

Children are drawing only. A <title>, link or tabindex inside them would add a tooltip or a focus stop the icon otherwise never has.

Right to left#

Arrows and chevrons pass mirror and flip when their resolved direction is right to left (a dir="ltr" island inside an Arabic page stays unflipped). Icons whose meaning has no direction (a check, a clock, a calendar) leave it off.

Alignment#

A 1em icon sits at vertical-align: -0.125em, a fixed step at middle. Override with an important utility, class="align-baseline!": two alignment utilities on the same element are resolved by Tailwind's order, not by which one you wrote last.

Props and content inputs#

On this page
NameTypeRequiredDefaultDescription
labelstring | nullYesNoneAccessible name, always passed. null marks the icon decorative (aria-hidden). A string makes it role="img" with that aria-label. A blank string is treated as null.
pathstringNoNonePath data (the d attribute) for a single-path icon. When set, children are ignored.
childrenSnippetNoNoneSVG child elements (paths, circles, groups) for a multi-shape icon. Used when path is absent.
viewBoxstringNo'0 0 24 24'The icon's drawing grid, four numbers. Its longer side converts each step's stroke weight into grid units.
size'xs' | 'sm' | 'md' | 'lg' | 'xl'NoNoneFixed square step: 12, 16, 20, 24 or 32 px. Omitted, the icon is 1em and scales with the surrounding text.
strokeModebooleanNofalseDraw with a round-capped, round-joined stroke in currentColor and no fill, for outline icon sets.
mirrorbooleanNofalseFlip the icon horizontally in right-to-left text. For arrows, chevrons and other directional icons.
classstringNoNoneExtra classes on the svg: a text colour, margins, or a size-* class that replaces the step.

Customization#

On this page

The icon has no colour of its own: it is drawn in currentColor, so set the text colour on it or any ancestor. Size through size or a size-* class; stroke weights live in the STROKE map.

  • Colour: pass class="text-amber-700" or set color on a parent. On a dark band or a cream page the icon follows the text with no change.
  • Size: omit size inside text so the icon is 1em and grows with a heading. Use sm (16 px) beside 13 to 14 px labels in buttons and dense rows, md (20 px) in navigation and lists, lg (24 px) only as an illustration.
  • Any size: pass class="size-10" (or any size-* utility). The steps are width and height attributes, so a class always wins. Stroke weight then scales with the drawing.
  • Stroke weight: the STROKE map at the top of the script holds each step's weight in pixels (1.25 at 12 px, 1.75 at 16, 1.5 at 20 and 24, 2 at 32). Change it there; 1em icons draw 2 units on a 24-unit grid.
  • Beside wrapping text: put the icon in a box one line tall (display: flex; align-items: center; height equal to the text's line height) so it stays level with the first line. usage.md has the pattern.
  • Icon sets: paste a single d attribute into path, or pass the set's child elements as children. A set drawn on a 20 or 16 unit grid passes its own viewBox.
  • Alignment: a 1em icon sits at vertical-align -0.125em and a fixed step at middle. To change it, pass an important utility such as class="align-baseline!", because two alignment utilities on one element resolve by Tailwind's order, not by which came last.

Accessibility#

On this page
  • label is a required prop typed string | null, so leaving out the accessibility decision is a TypeScript error rather than a silent default.
  • Decorative (label={null}): aria-hidden="true" and focusable="false", no role. Use it whenever adjacent text already says the same thing.
  • Meaningful (label="Overdue"): role="img" and aria-label on the svg. The component renders no <title>, so there is no hover tooltip and no duplicated name.
  • An icon alone inside a button or link is decorative; give the button its name (aria-label or visually hidden text), not the icon. usage.md shows both.
  • The icon is never focusable and carries no tooltip. A meaningful icon whose name should be visible on hover needs its own button or a visible label next to it.
  • Drawn in currentColor, so forced-colours mode repaints it with the system text colour, and contrast follows the text around it. A meaningful icon given its own colour needs 3:1 against the background.
  • Directional icons can pass mirror so they point the right way in right-to-left text.
  • Children are rendered as given. Pass drawing elements only (paths, circles, groups): a <title>, a link or a tabindex inside them would bring back a tooltip or a focus stop that the svg itself never has.

Known limitations

  • A blank label is treated as decorative rather than reported; check that dynamic labels are never empty when the icon carries meaning.
  • Colour alone is not a status: a red or amber icon still needs its label, or text beside it.

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 · 1 October 2026

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