Skip to content
Download ZIP

Blue background palette · 6.3 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_full_bleed_section_01 · version 1.0.0 · Blue background palette
Using the PageSugar MCP server, fetch component cmp_full_bleed_section_01 version 1.0.0 with variant "blue", 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
Blue background
Version
1.0.0
Digest
Full digest
sha256-1232d0c4b845fdee3617a07fb341bb8a47cd81ff9494aa04e91e2a5f6568176f
FullBleedSection.svelte Svelte · 8.5 KB Raw
<!--
	Full-bleed section: a band whose colour, image or video runs to both edges of the viewport while
	its content keeps the page's own edge (max-w-6xl inside 16, 24 and 32 px gutters, the same edge
	as a page container). Layers, back to front: the surface colour, the media, the overlay, the
	content. With breakout it escapes a narrower parent, such as an article column.
-->
<script lang="ts" module>
	export type FullBleedOverlay = 'none' | 'light' | 'dark' | 'gradient';
	export type FullBleedTone = 'default' | 'inverse';
	export type FullBleedWidth = 'default' | 'wide' | 'prose';
	export type FullBleedMinHeight = 'auto' | 'sm' | 'md' | 'screen';
	export type FullBleedContentAlign = 'center' | 'end';
</script>

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

	interface Props {
		/** Image, picture or video that fills the band. Decorative: give an img alt="". */
		background?: Snippet;
		/** Contrast layer between the media and the content. */
		overlay?: FullBleedOverlay;
		/** Text colour. Defaults to inverse under a dark or gradient overlay, otherwise default. */
		tone?: FullBleedTone;
		/** Escape a constrained parent and run to the viewport edges. */
		breakout?: boolean;
		/** Inner container width. */
		width?: FullBleedWidth;
		/** Minimum height. The band always grows with its content. */
		minHeight?: FullBleedMinHeight;
		/** Where the content sits when the band is taller than it. */
		contentAlign?: FullBleedContentAlign;
		/** Anchor id for #links. */
		id?: string;
		/** Id of the heading inside that names the band. */
		labelledby?: string;
		/** The band's content. */
		children: Snippet;
	}

	let {
		background,
		overlay = 'none',
		tone,
		breakout = false,
		width = 'default',
		minHeight = 'auto',
		contentAlign = 'end',
		id,
		labelledby,
		children
	}: Props = $props();

	/* Light text is the only legible choice over a dark scrim, so that is the default there. */
	const resolvedTone = $derived(
		tone ?? (overlay === 'dark' || overlay === 'gradient' ? 'inverse' : 'default')
	);

	/* The same measures as the page container, so the band's content starts on the page's edge. */
	const measure: Record<FullBleedWidth, string> = {
		default: 'max-w-6xl',
		wide: 'max-w-7xl',
		prose: 'max-w-2xl'
	};

	/* Minimums only, never heights: a long title or a translation pushes the band taller. */
	const heights: Record<FullBleedMinHeight, string> = {
		auto: '',
		sm: 'min-h-112',
		md: 'min-h-128 lg:min-h-160',
		screen: 'min-h-svh'
	};
</script>

<section
	{id}
	class={[
		'full-bleed-section relative isolate flex flex-col bg-(--full-bleed-tone-surface) px-4 py-16 text-(--full-bleed-tone-ink) sm:px-6 sm:py-24 lg:px-8 lg:py-32',
		contentAlign === 'end' ? 'justify-end' : 'justify-center',
		heights[minHeight],
		breakout &&
			'full-bleed-section--breakout ms-(--_bleed-margin) me-(--_bleed-margin) w-(--_viewport)'
	]}
	data-tone={resolvedTone}
	data-overlay={background ? overlay : undefined}
	data-align={contentAlign}
	aria-labelledby={labelledby || undefined}
>
	{#if background}
		<!-- Forced colours drop the overlay but not the image, so the media goes too and the text sits on the system canvas. -->
		<div
			class="full-bleed-section__media absolute inset-0 -z-20 forced-colors:hidden"
			aria-hidden="true"
		>
			{@render background()}
		</div>
		{#if overlay !== 'none'}
			<div
				class="full-bleed-section__overlay absolute inset-0 -z-10 bg-(image:--_scrim) forced-colors:hidden"
				aria-hidden="true"
			></div>
		{/if}
	{/if}
	<div class={['mx-auto w-full', measure[width]]}>
		{@render children?.()}
	</div>
</section>

<style>
	/*
	 * Public tokens: set --full-bleed-section-* on this band or any ancestor to retone it. The tone
	 * resolves them into four --full-bleed-tone-* values that content inside can read, so a heading
	 * or a hairline follows the band it sits on.
	 */
	.full-bleed-section {
		--_surface: var(--full-bleed-section-surface, #eff6ff);
		--_ink: var(--full-bleed-section-ink, #172554);
		--_muted: var(--full-bleed-section-muted, #1e40af);
		--_hairline: var(--full-bleed-section-hairline, #1e40af24);
		--_inverse-surface: var(--full-bleed-section-inverse-surface, #172554);
		--_on-inverse: var(--full-bleed-section-on-inverse, #ffffff);
		--_inverse-muted: var(--full-bleed-section-inverse-muted, #dbeafe);
		--_overlay: var(--full-bleed-section-overlay, #172554);
		--_inverse-scheme: var(--full-bleed-section-inverse-scheme, dark);
		--_viewport: var(--full-bleed-section-viewport, 100vw);

		/* Formula, read by ms-/me-: pulls each edge out from the parent's to the viewport's. */
		--_bleed-margin: calc(50% - var(--_viewport) / 2);

		--full-bleed-tone-surface: var(--_surface);
		--full-bleed-tone-ink: var(--_ink);
		--full-bleed-tone-muted: var(--_muted);
		--full-bleed-tone-hairline: var(--_hairline);
	}

	.full-bleed-section[data-tone='inverse'] {
		--full-bleed-tone-surface: var(--_inverse-surface);
		--full-bleed-tone-ink: var(--_on-inverse);
		--full-bleed-tone-muted: var(--_inverse-muted);
		--full-bleed-tone-hairline: color-mix(in oklab, var(--_on-inverse) 14%, transparent);
		color-scheme: var(--_inverse-scheme);
	}

	/*
	 * Overlay formulas, read by bg-(image:--_scrim). Light washes the media toward the surface for
	 * dark text; dark tints it evenly; gradient is an eased scrim that is darkest where the content
	 * sits (the bottom, or a band through the middle when the content is centred) and lighter away
	 * from it. Phones get the stronger stops, because the content fills more of a narrow band.
	 */
	.full-bleed-section[data-overlay='light'] {
		--_scrim: linear-gradient(color-mix(in oklab, var(--_surface) 82%, transparent) 0 0);
	}
	.full-bleed-section[data-overlay='dark'] {
		--_scrim: linear-gradient(color-mix(in oklab, var(--_overlay) 72%, transparent) 0 0);
	}
	.full-bleed-section[data-overlay='gradient'] {
		--_scrim: linear-gradient(
			to top,
			color-mix(in oklab, var(--_overlay) 90%, transparent) 0%,
			color-mix(in oklab, var(--_overlay) 82%, transparent) 30%,
			color-mix(in oklab, var(--_overlay) 58%, transparent) 55%,
			color-mix(in oklab, var(--_overlay) 22%, transparent) 80%,
			color-mix(in oklab, var(--_overlay) 8%, transparent) 100%
		);
	}
	.full-bleed-section[data-overlay='gradient'][data-align='center'] {
		--_scrim: linear-gradient(
			to top,
			color-mix(in oklab, var(--_overlay) 56%, transparent) 0%,
			color-mix(in oklab, var(--_overlay) 76%, transparent) 50%,
			color-mix(in oklab, var(--_overlay) 56%, transparent) 100%
		);
	}
	@media (max-width: 639.98px) {
		.full-bleed-section[data-overlay='gradient'] {
			--_scrim: linear-gradient(
				to top,
				color-mix(in oklab, var(--_overlay) 90%, transparent) 0%,
				color-mix(in oklab, var(--_overlay) 82%, transparent) 50%,
				color-mix(in oklab, var(--_overlay) 60%, transparent) 80%,
				color-mix(in oklab, var(--_overlay) 36%, transparent) 100%
			);
		}
		.full-bleed-section[data-overlay='gradient'][data-align='center'] {
			--_scrim: linear-gradient(
				to top,
				color-mix(in oklab, var(--_overlay) 70%, transparent) 0%,
				color-mix(in oklab, var(--_overlay) 82%, transparent) 50%,
				color-mix(in oklab, var(--_overlay) 70%, transparent) 100%
			);
		}
	}

	/*
	 * Media the consumer renders fills the band. Unlayered on purpose: if a utility on the img could
	 * undo the fill, the band would show a letterboxed picture. object-position is left alone, so a
	 * class such as object-top on the img still sets the focal point.
	 */
	.full-bleed-section__media > :global(*),
	.full-bleed-section__media > :global(picture > img) {
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
		object-fit: cover;
	}

	/*
	 * Chromium and Firefox draw an img's ::before only when it fails to load, and a sized broken img
	 * otherwise shows their icon and frame. This box covers them with the surface. Other engines may
	 * still show their own broken-image rendering; hide failed media yourself where that matters.
	 */
	.full-bleed-section__media > :global(img)::before,
	.full-bleed-section__media > :global(picture > img)::before {
		content: '';
		position: absolute;
		inset: 0;
		background: var(--full-bleed-tone-surface);
	}

	/*
	 * A breakout band is as wide as the viewport, and on a classic (non-overlay) scrollbar that is
	 * wider than the page by the scrollbar, which would scroll sideways. Clip the page's inline
	 * overflow, but only on pages that hold a breakout band. On body rather than html, so html stays
	 * visible and body's overflow still reaches the viewport: a modal's body { overflow: hidden }
	 * scroll lock keeps working, since it overrides this.
	 */
	:global(body:has(.full-bleed-section--breakout)) {
		overflow-x: clip;
	}
</style>

Presentational layout only: it paints a surface, fills the band with the media you pass, lays an overlay over it and keeps your content on the page's edge. It renders no heading or copy of its own, does not optimise or lazy-load images, and builds no video controls or parallax.

Suggested location
src/lib/components/full-bleed-section-01
Required props
children

Limitations

  • Renders no heading. Pass one as a child and give its id to labelledby, or the band stays an unnamed generic container.
  • The overlays are tuned for typical photographs and illustrations, not guaranteed for every image: check your text against the brightest part of your media that sits behind it.
  • Breakout sizes the band to 100vw, which on a classic (non-overlay) scrollbar is wider than the page, so the component sets overflow-x: clip on body while a breakout band is on the page. Below the max width, the gutters there narrow by half a scrollbar. Chrome 145+ measures exactly with html { scrollbar-gutter: stable }; elsewhere set --full-bleed-section-viewport.
  • If media fails to load, the surface colour shows through. Chromium and Firefox show nothing else; other engines may draw their own broken-image mark, so remove failed media yourself where that matters.
  • Breakout centres on its parent, so the parent must be centred on the page for the band to land on both viewport edges.
  • A background video is yours to control: it must be muted, and anything that plays for more than five seconds needs a pause button (WCAG 2.2.2). None is built in.
  • In forced-colours mode the media and overlay are hidden and the band shows the system canvas, since the overlay cannot be kept and text over a bare image would be unreadable.
  • Light appearance by default. The tone tokens retone it for a dark page, but no dark mode is declared or selected automatically.

Example

Svelte
<script lang="ts">
	import FullBleedSection from '$lib/components/full-bleed-section-01/FullBleedSection.svelte';
</script>

<FullBleedSection overlay="gradient" minHeight="md" labelledby="timelines-title">
	{#snippet background()}
		<img src="/images/timeline.jpg" alt="" width="1600" height="1000" />
	{/snippet}
	<h2 id="timelines-title" class="text-4xl font-semibold tracking-tight text-(--full-bleed-tone-ink)">
		Move one date and the whole quarter follows
	</h2>
	<p class="mt-4 max-w-xl text-lg text-(--full-bleed-tone-muted)">
		Timelines come with Team at $12 per seat per month.
	</p>
</FullBleedSection>

A band that runs edge to edge#

Copy FullBleedSection.svelte into src/lib/components/full-bleed-section-01/. Pass your media in the background snippet and your content as children. The media fills the band; the content keeps the page's edge (max-w-6xl inside 16, 24 and 32 px gutters), so it lines up with the sections around it.

Svelte
<script lang="ts">
	import FullBleedSection from '$lib/components/full-bleed-section-01/FullBleedSection.svelte';
</script>

<FullBleedSection overlay="gradient" minHeight="md" labelledby="timelines-title">
	{#snippet background()}
		<img src="/images/timeline.jpg" alt="" width="1600" height="1000" class="object-[50%_30%]" />
	{/snippet}
	<h2
		id="timelines-title"
		class="text-4xl font-semibold tracking-tight text-(--full-bleed-tone-ink)"
	>
		Move one date and the whole quarter follows
	</h2>
</FullBleedSection>

Without a background the band is a plain colour field: the default tone's surface (#f4f4f5) or the inverse tone's (#09090b).

Overlays and tone#

overlay What it does Tone it defaults to
none The media as it is default
light Washes the media 82% toward the surface, for dark text default
dark An even 72% tint of the overlay colour inverse
gradient An eased scrim, darkest where the content sits, lighter above it inverse

The gradient follows contentAlign: with content at the bottom (the default) it rises from the bottom edge and leaves the top of the image almost untouched; with contentAlign="center" it is darkest through the middle and lighter toward both edges. Phones get stronger stops because the content covers more of a narrow band.

Pass tone to override the default. Whatever you pick, check your text against the brightest part of your image that sits behind it; the component cannot see the image.

Content that follows the tone#

Variable Default tone Inverse tone
--full-bleed-tone-ink #18181b #fafafa
--full-bleed-tone-muted #52525b #e4e4e7
--full-bleed-tone-hairline 8% black 14% of on-inverse
--full-bleed-tone-surface #f4f4f5 #09090b

Use them in your markup (text-(--full-bleed-tone-muted)) and your heading follows the band.

Breaking out of a column#

Inside an article or any other narrow, centred parent, breakout pulls the band out to the viewport edges. Set width="prose" when the parent is a 672 px column, so the band's text stays on the article's edge:

Svelte
<article class="mx-auto max-w-2xl">
	<p>…</p>
	<FullBleedSection breakout width="prose" overlay="dark">
		{#snippet background()}<img src="/images/pool.jpg" alt="" />{/snippet}
		<h2>Nothing gets a date until it has an owner</h2>
	</FullBleedSection>
	<p>…</p>
</article>

The band is 100vw wide. With a classic scrollbar (Windows, or macOS set to always show scrollbars) that is wider than the page, so while a breakout band is on the page the component sets body { overflow-x: clip }. It sits on body, not html, so a modal's body { overflow: hidden } scroll lock still reaches the viewport and wins. Below the container's max width the band's gutters narrow by half a scrollbar, because CSS cannot measure the scrollbar; Chrome 145 and later measure exactly when you set html { scrollbar-gutter: stable }. If your html is a size container, set --full-bleed-section-viewport: 100cqi instead.

The parent has to be centred on the page, because the band centres on its parent.

Video#

A <video autoplay muted loop playsinline> in the background snippet fills the band like an image. The component builds no controls: anything that plays for more than five seconds needs a visible pause button (WCAG 2.2.2), and under prefers-reduced-motion it should not autoplay. Give it a poster, which also stands in while it loads.

Retoning#

Variable Default Draws
--full-bleed-section-surface #f4f4f5 Default tone band and light overlay
--full-bleed-section-ink #18181b Text on the default tone
--full-bleed-section-muted #52525b Secondary text on the default tone
--full-bleed-section-hairline rgb(0 0 0 / 0.08) Hairline value on the default tone
--full-bleed-section-inverse-surface #09090b Inverse tone band
--full-bleed-section-on-inverse #fafafa Text on the inverse tone
--full-bleed-section-inverse-muted #e4e4e7 Secondary text on the inverse tone
--full-bleed-section-overlay #09090b Dark and gradient overlays
--full-bleed-section-inverse-scheme dark color-scheme on the inverse tone
--full-bleed-section-viewport 100vw Width a breakout band spans

Set --full-bleed-section-overlay to a deep brand colour to tint the image toward the brand. On a dark page:

Svelte
<div
	style="
		--full-bleed-section-surface: #18181b;
		--full-bleed-section-ink: #fafafa;
		--full-bleed-section-muted: #a1a1aa;
		--full-bleed-section-hairline: rgb(255 255 255 / 0.1);
		--full-bleed-section-inverse-surface: #09090b;
	"
>
	<!-- bands -->
</div>

Re-check contrast after any change: 4.5:1 for text against each surface and against the overlaid media.

Props and content inputs#

On this page
NameTypeRequiredDefaultDescription
childrenSnippetYesNoneThe band's content, rendered inside the inner container above the media and overlay.
backgroundSnippetNoNoneAn img, picture or video that fills the band with object-cover. Decorative: give an img alt="". Omitted, the surface colour fills the band.
overlay'none' | 'light' | 'dark' | 'gradient'No'none'Contrast layer over the media: none, a wash toward the surface for dark text, an even dark tint, or an eased scrim darkest where the content sits. Applies only with a background.
tone'default' | 'inverse'NoNoneText colour and fallback surface. Unset, it is inverse under a dark or gradient overlay and default otherwise.
breakoutbooleanNofalseEscape a constrained parent, such as an article column, and run to the viewport edges.
width'default' | 'wide' | 'prose'No'default'Inner container: max-w-6xl, max-w-7xl or max-w-2xl of content inside 16/24/32 px gutters, the page container's measures.
minHeight'auto' | 'sm' | 'md' | 'screen'No'auto'Minimum height: auto (padding and content), sm 448 px, md 512 px rising to 640 px from lg, or the small viewport height. Content always grows past it.
contentAlign'center' | 'end'No'end'Where the content sits when the band is taller than it: anchored to the bottom, or centred in the height.
idstringNoNoneAnchor id for #links.
labelledbystringNoNoneId of the heading inside that names the band. When set, the band is exposed as a named region; when omitted, no aria-labelledby is rendered.

Customization#

On this page

Pick the overlay, tone, width and height through props. Retone the surfaces, text and overlay through --full-bleed-section-* variables, and read the four --full-bleed-tone-* values inside the band so your content follows its tone.

  • Content colours: inside the band, use var(--full-bleed-tone-ink) for headings and body text, var(--full-bleed-tone-muted) for secondary text, var(--full-bleed-tone-hairline) for dividers and var(--full-bleed-tone-surface) for anything that must match the fallback surface. They change with the tone.
  • Media: pass an img, picture or video in the background snippet. It fills the band with object-cover; set the focal point with object-position on the element (object-top, object-[50%_30%]).
  • Overlay: --full-bleed-section-overlay is the dark and gradient overlays' colour (near-black by default). Set it to a deep brand colour to tint the image toward the brand. The light overlay is mixed from --full-bleed-section-surface.
  • Surfaces: --full-bleed-section-surface is the default tone's band colour and --full-bleed-section-inverse-surface the inverse tone's. Either shows when there is no media or the media fails to load, so pick the one closest to your image.
  • Text: --full-bleed-section-ink and --full-bleed-section-muted on the default tone; --full-bleed-section-on-inverse and --full-bleed-section-inverse-muted on the inverse tone. Keep 4.5:1 against the surface and against the overlaid media.
  • Native controls: --full-bleed-section-inverse-scheme sets color-scheme on the inverse tone (dark by default).
  • Breakout width: --full-bleed-section-viewport is the width a breakout band spans (100vw). If html is a size container, set it to 100cqi to measure without the scrollbar.
  • Dark page retone: on the page wrapper set surface #18181b, ink #fafafa, muted #a1a1aa, hairline rgb(255 255 255 / 0.1), inverse-surface #09090b (all --full-bleed-section-*). usage.md has the snippet.
  • Heights and widths: the measure and heights maps at the top of the script hold the Tailwind classes for each preset; edit them there to change the scale.

Public CSS variables

VariableToken
--full-bleed-section-surfacesurface
--full-bleed-section-inkink
--full-bleed-section-mutedmuted
--full-bleed-section-hairlinehairline
--full-bleed-section-inverse-surfaceinverseSurface
--full-bleed-section-on-inverseonInverse
--full-bleed-section-inverse-mutedinverseMuted
--full-bleed-section-overlayoverlay
--full-bleed-section-inverse-schemeinverseScheme
--full-bleed-section-viewportviewport

Accessibility#

On this page
  • The root is a section element. With labelledby it carries aria-labelledby and is exposed as a named region; without it no aria attribute is rendered, so it stays a generic container rather than an unnamed landmark.
  • The media and overlay layers are aria-hidden: the background is decorative. Give an img alt=""; if the picture carries information, put it in the content as text or a figure instead.
  • The overlay exists for contrast, but the component cannot see your image. Check ink and muted text at 4.5:1 against the brightest part of the media behind them, at every width: the band reflows, so text crosses different parts of the image on a phone.
  • Without media the band's surface carries the contrast: ink #18181b on #f4f4f5 16.1:1, muted #52525b 7.0:1; #fafafa on #09090b 19.1:1, #e4e4e7 15.7:1.
  • In forced-colours mode the media and overlay are hidden, so text sits on the system canvas in system colours.
  • A background video must be muted, and one that plays for more than five seconds needs a visible pause control (WCAG 2.2.2); honour prefers-reduced-motion by not autoplaying it.
  • Layout uses logical properties and a symmetric breakout, and mirrors under dir="rtl".

Known limitations

  • Contrast figures cover the shipped palettes and fixture media only; re-check with your own images and any retoned token.
  • Links inside the band are the consumer's markup: on the inverse tone, make inline links distinguishable by more than colour and give focus rings a colour that clears 3:1 against the band, such as var(--full-bleed-tone-ink).

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.