Skip to content
Download ZIP

Neutral palette · 4.8 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_footer_minimal_01 · version 1.0.0 · Neutral palette
Using the PageSugar MCP server, fetch component cmp_footer_minimal_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-1253cefb602cac5bd8afc3e33a78e3dad2468d0d06962fe521e69c425cb5efe2
FooterMinimal.svelte Svelte · 6.2 KB Raw
<script lang="ts" module>
	export interface FooterLink {
		label: string;
		href: string;
	}
</script>

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

	interface Props {
		/** The organisation's name as text, or your own logo snippet (give its image a text alternative). */
		brand: string | Snippet;
		/** Where the brand links, usually the home page. Omitted, the brand is not a link. */
		brandHref?: string;
		/** Complete copyright text, year included. The component never reads the clock. */
		copyright: string;
		/** A handful of main destinations; four to six read best. */
		links?: FooterLink[];
		/** Policy destinations such as Privacy and Terms, set in the small print. */
		legalLinks?: FooterLink[];
		/** Brand at the start and links at the end, or everything centred and stacked. */
		layout?: 'inline' | 'centered';
		/** Accessible name of the navigation holding `links`. */
		navLabel?: string;
		/** Accessible name of the navigation holding `legalLinks`. */
		legalLabel?: string;
	}

	let {
		brand,
		brandHref,
		copyright,
		links = [],
		legalLinks = [],
		layout = 'inline',
		navLabel = 'Footer',
		legalLabel = 'Legal'
	}: Props = $props();

	const centered = $derived(layout === 'centered');
	/* With nothing to divide, the copyright joins the brand's row and the small-print tier goes. */
	const bare = $derived(links.length === 0 && legalLinks.length === 0);

	/*
	 * One link style for both navs: muted at rest, ink on hover. A 28 by 24 px minimum on a fine
	 * pointer, its row level with the brand's; on a coarse pointer every link is at least 44 by 44 px.
	 */
	const linkClass = $derived([
		'inline-flex max-w-full min-h-11 min-w-11 items-center rounded-sm text-(--_muted) transition-colors duration-150 ease-[cubic-bezier(.2,0,0,1)] hover:text-(--_ink) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--_accent) pointer-fine:min-h-7 pointer-fine:min-w-6',
		centered && 'justify-center'
	]);
</script>

{#snippet anchor(link: FooterLink)}
	<a href={link.href} class={linkClass}>
		<span class="min-w-0 [overflow-wrap:anywhere]">{link.label}</span>
	</a>
{/snippet}

{#snippet brandBlock()}
	{#if brandHref}
		<a
			href={brandHref}
			class="inline-flex min-h-11 max-w-full min-w-11 items-center rounded-sm text-(--_ink) transition-colors duration-150 ease-[cubic-bezier(.2,0,0,1)] hover:text-(--_muted) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--_accent) pointer-fine:min-h-7"
		>
			{@render brandMark()}
		</a>
	{:else}
		<div class="flex min-h-7 max-w-full items-center text-(--_ink)">
			{@render brandMark()}
		</div>
	{/if}
{/snippet}

{#snippet brandMark()}
	{#if typeof brand === 'string'}
		<span
			class="min-w-0 text-lg leading-7 font-semibold tracking-[-0.015em] text-balance [overflow-wrap:anywhere] rtl:tracking-normal"
		>
			{brand}
		</span>
	{:else}
		{@render brand()}
	{/if}
{/snippet}

{#snippet copyrightLine()}
	<p class="max-w-[30em] min-w-0 text-balance [overflow-wrap:anywhere] tabular-nums">
		{copyright}
	</p>
{/snippet}

{#snippet legalNav()}
	<nav aria-label={legalLabel} class="min-w-0">
		<ul role="list" class={['flex flex-wrap gap-x-6', centered && 'justify-center']}>
			{#each legalLinks as link, index (index)}
				<li class="min-w-0">{@render anchor(link)}</li>
			{/each}
		</ul>
	</nav>
{/snippet}

<footer
	class="footer-minimal border-t border-(--_hairline) bg-(--_surface) px-4 py-12 text-start sm:px-6 sm:py-16 lg:px-8"
>
	<div class="mx-auto max-w-6xl">
		{#if centered}
			<div class="flex flex-col items-center gap-6 text-center">
				{@render brandBlock()}
				{#if links.length}
					<nav aria-label={navLabel} class="max-w-full min-w-0">
						<ul role="list" class="flex flex-wrap justify-center gap-x-6 text-sm leading-5">
							{#each links as link, index (index)}
								<li class="min-w-0">{@render anchor(link)}</li>
							{/each}
						</ul>
					</nav>
				{/if}
				{#if bare}
					<div class="text-[0.8125rem] leading-5 text-(--_muted)">
						{@render copyrightLine()}
					</div>
				{/if}
			</div>
			{#if !bare}
				<div
					class="mt-8 flex flex-col items-center gap-3 border-t border-(--_hairline) pt-8 text-center text-[0.8125rem] leading-5 text-(--_muted)"
				>
					{#if legalLinks.length}{@render legalNav()}{/if}
					{@render copyrightLine()}
				</div>
			{/if}
		{:else}
			<!-- Brand and destinations share a 28 px first row, so their first lines sit level. -->
			<div class="flex flex-col items-start gap-6 md:flex-row md:justify-between md:gap-8">
				<div class="max-w-full min-w-0 md:max-w-sm md:shrink-0">
					{@render brandBlock()}
				</div>
				{#if bare}
					<div class="text-[0.8125rem] leading-5 text-(--_muted) md:leading-7">
						{@render copyrightLine()}
					</div>
				{:else if links.length}
					<nav aria-label={navLabel} class="w-full min-w-0 md:w-auto md:flex-1">
						<ul
							role="list"
							class="grid grid-cols-2 gap-x-6 text-sm leading-5 sm:flex sm:flex-wrap md:justify-end"
						>
							{#each links as link, index (index)}
								<li class="min-w-0">{@render anchor(link)}</li>
							{/each}
						</ul>
					</nav>
				{/if}
			</div>
			{#if !bare}
				<!-- The small print repeats the row's two edges: copyright under the brand, policies under the links. -->
				<div
					class="mt-8 flex flex-col items-start gap-3 border-t border-(--_hairline) pt-8 text-[0.8125rem] leading-5 text-(--_muted) md:flex-row md:items-baseline md:justify-between md:gap-8"
				>
					{@render copyrightLine()}
					{#if legalLinks.length}{@render legalNav()}{/if}
				</div>
			{/if}
		{/if}
	</div>
</footer>

<style>
	/* Public tokens: set --footer-minimal-* on this footer or any ancestor to retone it. */
	.footer-minimal {
		--_accent: var(--footer-minimal-accent, #18181b);
		--_on-accent: var(--footer-minimal-on-accent, #ffffff);
		--_ink: var(--footer-minimal-ink, #18181b);
		--_muted: var(--footer-minimal-muted, #52525b);
		--_hairline: var(--footer-minimal-hairline, rgb(0 0 0 / 0.08));
		--_surface: var(--footer-minimal-surface, transparent);
	}

	/*
	 * Selected text takes the accent pair, so a copied company name reads in the brand's colours.
	 * CSS, because it styles every element the footer renders rather than one.
	 */
	.footer-minimal ::selection {
		background-color: var(--_accent);
		color: var(--_on-accent);
	}
</style>

Presentational only: the brand, links and copyright are rendered as supplied. It does not write policy content, include social links (compose a social links group beside it), check that links resolve or work out the copyright year; pass the complete copyright text. No runtime packages beyond Svelte and Tailwind CSS.

Suggested location
src/lib/components/footer-minimal-01
Required props
brandcopyright

Limitations

  • Built for a handful of links. Past about eight destinations the row wraps onto several lines; a multi-column footer suits a larger set.
  • With no links and no legal links, the copyright moves onto the brand's row and the hairline is dropped.
  • Every link opens in the same tab; there is no external-link treatment.
  • Light appearance by default with a transparent surface, so it takes the page's background. The dark palette retones it through its variables; no dark mode is declared or selected automatically.

Example

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

<FooterMinimal
	brand="Example Co"
	brandHref="/"
	links={[
		{ label: 'Features', href: '/features' },
		{ label: 'Pricing', href: '/pricing' },
		{ label: 'Contact', href: '/contact' }
	]}
	legalLinks={[
		{ label: 'Privacy', href: '/privacy' },
		{ label: 'Terms', href: '/terms' }
	]}
	copyright="© 2026 Example Co Ltd."
/>

Filling it in#

Copy FooterMinimal.svelte into src/lib/components/footer-minimal-01/. The FooterLink type is exported from its module script.

  • brand is your organisation's name as a string, or a snippet holding your logo. A snippet renders as given, inside the brand link when brandHref is set and inside a plain div otherwise. Keep it free of links and buttons of its own, and give any image a text alternative (or keep the mark aria-hidden and put the name beside it as text) so the link has a name.
  • links are the main destinations. Four to six read best; past about eight, use a multi-column footer.
  • legalLinks are the policy pages. They sit in the small print, in their own navigation named legalLabel.
  • copyright is the whole string, year included. The component never computes the year, so the server and the browser always render the same text.

Leave out links and legalLinks and no navigation renders; the copyright joins the brand's row.

Layout#

Width layout="inline" (default) layout="centered"
under 640 Brand, then links two to a row, then the copyright and policies, all at the start Brand, links, policies and copyright on the centre line
640 to 767 As above, with the links in one wrapping row As above
768 and up Brand at the start, links at the end; copyright and policies on the same edges As above

Retoning#

Variable Default Draws
--footer-minimal-ink #18181b Brand, link hover
--footer-minimal-muted #52525b Links, copyright
--footer-minimal-hairline rgb(0 0 0 / 0.08) Top edge, line above the small print
--footer-minimal-accent #18181b Focus ring, selection fill
--footer-minimal-on-accent #ffffff Selected text
--footer-minimal-surface transparent Background (the page shows through it)

A dark band:

Svelte
<div
	style="
		--footer-minimal-surface: #09090b;
		--footer-minimal-ink: #fafafa;
		--footer-minimal-muted: #a1a1aa;
		--footer-minimal-hairline: rgb(255 255 255 / 0.1);
		--footer-minimal-accent: #fafafa;
		--footer-minimal-on-accent: #18181b;
	"
>
	<FooterMinimal {brand} {links} {legalLinks} {copyright} />
</div>

A logo beside the name:

Svelte
{#snippet logo()}
	<span class="inline-flex items-center gap-2 text-lg leading-7 font-semibold">
		<img src="/logo.svg" alt="" class="size-6" />
		Example Co
	</span>
{/snippet}

<FooterMinimal brand={logo} brandHref="/" {copyright} />

Props and content inputs#

On this page
NameTypeRequiredDefaultDescription
brandstring | SnippetYesNoneThe organisation's name as text, or your own logo snippet. The snippet renders inside the brand link (with brandHref) or a div, so keep it non-interactive and give a logo image a text alternative.
brandHrefstringNoNoneWhere the brand links, usually '/'. Omitted, the brand is plain content, not a link.
layout'inline' | 'centered'No'inline'Brand at the start and links at the end from 768 px, stacked below; or everything centred and stacked.
navLabelstringNo'Footer'Accessible name of the navigation holding links.
legalLabelstringNo'Legal'Accessible name of the navigation holding legalLinks. Keep it different from navLabel.

Customization#

On this page

Change content through props, retone the footer through six --footer-minimal-* variables (accent, on-accent, ink, muted, hairline, surface), and edit Tailwind classes in the source for spacing or the breakpoint.

  • Text: --footer-minimal-ink sets the brand and link hover; --footer-minimal-muted sets the links and small print. Keep muted at 4.5:1 against the surface.
  • Hairline: --footer-minimal-hairline draws the footer's top edge and the line above the small print.
  • Accent: --footer-minimal-accent is the focus ring and the text-selection fill, with --footer-minimal-on-accent as the selected text. Keep the accent at 3:1 against the surface.
  • Surface: --footer-minimal-surface is the footer background, transparent by default so it takes the page's colour.
  • Dark band: surface #09090b, ink #fafafa, muted #a1a1aa, hairline #ffffff1a, accent #fafafa, on-accent #18181b. usage.md has the snippet.
  • Cream page: leave surface transparent and set hairline to rgb(68 64 60 / 0.12) so the lines warm with the page.
  • Logo: pass a snippet as brand for a mark beside the name; keep it about 24 px tall so it sits in the 28 px brand row.
  • Layout: layout='centered' stacks everything on the centre line; the inline layout switches to one row at the md breakpoint (768 px).
  • Copyright: pass the full string, including the year; the component does not compute it, so server and client renders always agree.

Public CSS variables

VariableToken
--footer-minimal-accentaccent
--footer-minimal-on-accentonAccent
--footer-minimal-inkink
--footer-minimal-mutedmuted
--footer-minimal-hairlinehairline
--footer-minimal-surfacesurface

Accessibility#

On this page
  • The root is a footer element, which is the contentinfo landmark when it is not inside an article, aside, main, nav or section.
  • links sit in a nav named by navLabel ('Footer') and legalLinks in a second nav named by legalLabel ('Legal'), each holding a list. A nav whose links are empty is not rendered.
  • No separator characters sit between links; spacing separates them, so screen readers read only the labels.
  • Links are muted at rest (#52525b, 7.7:1 on white) and turn ink on hover; focus shows a two-pixel accent outline offset by two pixels on :focus-visible only.
  • Every link, and the brand when linked, is at least 28 px tall on a fine pointer (WCAG 2.5.8 asks for 24) and at least 44 by 44 px on a coarse pointer.
  • When brand is a snippet, it renders as given inside the brand link when brandHref is set, and inside a plain div otherwise. Keep it non-interactive (no links or buttons of its own) and give any logo image a text alternative, or mark the image aria-hidden and put the name beside it as text, so the link has a name.
  • The layout uses logical properties and mirrors under dir="rtl"; the brand name's tracking resets there.

Known limitations

  • Contrast ratios are computed for the shipped palettes; re-check any retoned token (4.5:1 for text, 3:1 for the focus ring).
  • The surface is transparent by default, so text contrast depends on your page background until you set --footer-minimal-surface.

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 · 30 September 2026

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