Skip to content
Download ZIP

Blue confirmation palette · 10.1 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_copyable_value_01 · version 1.0.0 · Blue confirmation palette
Using the PageSugar MCP server, fetch component cmp_copyable_value_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 confirmation
Version
1.0.0
Digest
Full digest
sha256-5051c22336748a111282464f818035956eca0997d1c61509d6df5d69a602896e

This component needs all 2 files. Download the ZIP

copy-text.ts TypeScript · 2.0 KB Raw
/**
 * Write text to the clipboard from inside a click handler. Tries the async Clipboard API first,
 * then the legacy execCommand path for insecure contexts and browsers or frames that block the
 * API. Resolves to whether the text reached the clipboard; it never throws. Pass cancelled to
 * skip the fallback when the caller has unmounted or moved on while the first try was pending.
 * Copy this file as it is into any other component that needs a copy action.
 */
export async function copyText(text: string, cancelled?: () => boolean): Promise<boolean> {
	if (typeof navigator === 'undefined' || typeof document === 'undefined') return false;

	if (navigator.clipboard?.writeText) {
		try {
			await navigator.clipboard.writeText(text);
			return true;
		} catch {
			/* Permission denied or blocked by the frame: try the legacy path. */
		}
	}
	if (cancelled?.()) return false;
	return legacyCopy(text);
}

/** Copies through an off-screen textarea, then restores focus and the reader's selection. */
function legacyCopy(text: string): boolean {
	if (typeof document.execCommand !== 'function') return false;

	const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
	const selection = document.getSelection();
	const ranges = selection
		? Array.from({ length: selection.rangeCount }, (_, i) => selection.getRangeAt(i))
		: [];

	const area = document.createElement('textarea');
	area.value = text;
	area.setAttribute('readonly', '');
	area.setAttribute('aria-hidden', 'true');
	area.tabIndex = -1;
	Object.assign(area.style, {
		position: 'fixed',
		insetBlockStart: '0',
		insetInlineStart: '0',
		opacity: '0',
		pointerEvents: 'none'
	});
	document.body.append(area);
	area.select();

	let ok: boolean;
	try {
		ok = document.execCommand('copy');
	} catch {
		ok = false;
	}

	area.remove();
	active?.focus({ preventScroll: true });
	if (selection) {
		selection.removeAllRanges();
		for (const range of ranges) selection.addRange(range);
	}
	return ok;
}

Pass a label and the value; the component renders it as selectable text with a Copy button that writes exactly that value, confirms in place and announces the result. Masking only hides the value on screen: it is still in the page source and the DOM, so never use it to protect a secret the viewer should not have. It does not generate, rotate or fetch keys.

Suggested location
src/lib/components/copyable-value-01
Required props
valuelabel

Limitations

  • Copying needs JavaScript. Before hydration, or with scripts off, the value is still ordinary text that can be selected and copied by hand.
  • Browsers only allow clipboard writes from a user action on a secure (https or localhost) page. Elsewhere the execCommand fallback is tried, and if that is blocked too the failure message shows and the value is selected.
  • Masking is visual only. The value is in the HTML and the DOM, and Copy writes it without revealing it.
  • Middle truncation is by width: the start shortens with an ellipsis and the last tailLength characters stay. Pass display for a fixed short form instead; manual selection then copies the display text.
  • The boxed layout lays out the failure message invisibly from the first render, so a failure moves nothing below. The inline layout keeps its row compact: a copy shows a check in the button, and a failure adds its message on a line under the row.
  • A revealed masked value is shown whole on one line that scrolls sideways (and takes keyboard focus to scroll), rather than truncated.

Example

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

<div class="max-w-sm">
	<CopyableValue label="Order number" value="HAL-2026-08417" />
	<CopyableValue
		label="API key"
		value="hlc_live_4fT9kQ2mX8vR1nB7cW3zP6"
		mono
		mask
		onCopy={({ ok }) => console.log(ok ? 'copied' : 'copy failed')}
	/>
</div>

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.

Copyable value#

One value, one Copy button, one confirmation. Use it for order and invoice numbers on receipts, IDs and keys on settings pages, wallet addresses, and codes people paste somewhere else.

Boxed or inline#

layout="boxed" (the default) sets the value in a tinted read-only well with Copy attached inside the same hairline, and holds one line under the field for a failure message. Use it on its own or stacked in a settings panel.

layout="inline" runs the value in a row of text with a 28 px icon button, for a details list, a table cell or a sentence. Set hideLabel when the row already names the value; the label still names the group and the button for screen readers.

What gets copied#

Always value, exactly. display and truncation change only what is drawn:

  • truncate="middle" shortens by width with a CSS ellipsis and keeps the last tailLength characters, so the ellipsis is never copied, even when someone selects the text by hand.
  • truncate="end" is a plain end ellipsis; truncate="wrap" breaks the value across lines.
  • display draws a fixed short form of your own. Copy still writes value, but a manual selection copies the display text, so prefer truncate when people might select it.

When copying fails#

The Clipboard API needs a user action on a secure page. copy-text.ts tries it, then the legacy execCommand('copy') path. If both fail, failedLabel shows and is announced, the value is selected so Ctrl+C or the context menu copies it, and onCopy receives { ok: false }. The failure stays until the next attempt; a success resets after resetAfter milliseconds.

copy-text.ts has no dependencies on the component. Copy it into a code block or command snippet that needs the same behaviour.

Masking#

mask swaps the value for twelve dots and adds a Show toggle. It is a screen-sharing courtesy, not security: the value is in the HTML, and Copy writes it without revealing it. Bind revealed to control or read the toggle.

Props and content inputs#

On this page
NameTypeRequiredDefaultDescription
valuestringYesNoneThe exact text written to the clipboard. An empty string shows emptyLabel and disables Copy.
labelstringYesNoneNames the value and both buttons: 'Copy {label}' and 'Show {label}'. Visible unless hideLabel is set.
displaystringNoNoneShown instead of value when they differ, such as a shortened form. Copy still writes value, and screen readers hear value.
monobooleanNofalseMonospace type for identifiers, keys and addresses.
maskbooleanNofalseShows dots and a Show toggle (aria-pressed). Revealed, the value is shown whole on one line that scrolls. Visual only: the value is still in the page.
revealedbooleanNofalseWhether a masked value is showing. Bindable.
layout'boxed' | 'inline'No'boxed''boxed' sets the value in a field with Copy attached; 'inline' runs it in a row of text with a small icon button.
truncate'middle' | 'end' | 'wrap'No'middle'How a value too long for its width shortens. 'middle' keeps the start and the last tailLength characters; 'wrap' breaks it across lines.
tailLengthnumberNo6Characters kept at the end when truncate is 'middle'.
hideLabelbooleanNofalseKeep the label for assistive technology only, when the row or heading already names the value.
copyLabelstringNo'Copy'Visible button text and the start of its accessible name.
copiedLabelstringNo'Copied'Confirmation after a successful copy, shown in the button (and in its name) and announced.
failedLabelstringNo'Copy failed. Copy the text manually.'Message after a failed copy, shown under the field and announced. The value is selected when it is visible.
emptyLabelstringNo'Not available yet'Shown in the field, muted, when value is empty, such as a tracking number not issued yet.
showLabelstringNo'Show'Start of the reveal toggle's name: 'Show {label}'.
hiddenLabelstringNo'Hidden'Read by screen readers in place of a masked value.
resetAfternumberNo2000Milliseconds before Copied returns to rest; 0 keeps it. A failure stays until the next attempt.
valueDir'ltr' | 'rtl' | 'auto'No'ltr'Direction of the value itself. Identifiers stay left to right on a right-to-left page.
onCopy(result: { ok: boolean }) => voidNoNoneCalled after every copy attempt with whether the text reached the clipboard.
classstringNoNoneExtra classes on the root, for placement.

Customization#

On this page

Change every word through props and retone the field through six --copyable-value-* CSS variables. The well, hover and press fills are mixed from surface and ink, so they follow any retone.

  • Accent: --copyable-value-accent colours the focus ring and the Copied check. The blue palette sets it to #1d4ed8; keep it at 3:1 against the surface.
  • Danger: --copyable-value-danger colours the failure mark and message. The default is red-700 (6.5:1 on white).
  • Neutrals: --copyable-value-ink is the label, value and button text, --copyable-value-muted the inline label and icon buttons, --copyable-value-hairline the field's edge and the divider before each button, --copyable-value-surface the button fill (the value well is surface mixed 3% toward ink).
  • Worked retone for a #09090b band: --copyable-value-surface: #18181b; --copyable-value-ink: #fafafa; --copyable-value-muted: #a1a1aa; --copyable-value-hairline: rgb(255 255 255 / 0.12); --copyable-value-accent: #fafafa; --copyable-value-danger: #f87171.
  • Cream page such as a bakery's #fbf7f0: set --copyable-value-surface: #fffdf9 and leave the hairline alone; it is alpha black, so it tints with the page.
  • Width: the boxed field fills its container. Put it in a max-w-sm or max-w-md column, or a grid cell; stack several with a 12 px gap (the held message line adds the rest).
  • Inline: use layout 'inline' inside a details list, table cell or sentence, and hideLabel when the row already names the value.
  • Long values: keep truncate 'middle' for hashes and addresses, raise tailLength when the end is what people check, or use 'wrap' for links people should read in full.
  • Languages: translate copyLabel, copiedLabel, failedLabel, showLabel and hiddenLabel with the page. Keep valueDir 'ltr' for identifiers on right-to-left pages.

Public CSS variables

VariableToken
--copyable-value-accentaccent
--copyable-value-dangerdanger
--copyable-value-inkink
--copyable-value-mutedmuted
--copyable-value-hairlinehairline
--copyable-value-surfacesurface

Accessibility#

On this page
  • The root is a group named by the label, which stays in the accessibility tree when hideLabel hides it visually.
  • The Copy button is named 'Copy {label}' (copyLabel then label), and 'Copied {label}' while the boxed button shows Copied, so the name always starts with the visible text. Focus stays on it after a copy.
  • A polite role=status region is in the page from the first render; it announces copiedLabel or failedLabel after each attempt, and a second copy is announced again.
  • Success is a check mark and the word Copied, and failure an icon and a sentence, never colour alone.
  • Middle-truncated or display text is hidden from assistive technology and the whole value is read from a visually hidden copy, which is excluded from manual selection so a drag-copy is not doubled.
  • A masked value is hidden from assistive technology, which hears hiddenLabel; the Show toggle keeps one name and reports its state with aria-pressed.
  • Buttons are 40 px tall in the boxed field (44 px on touch screens) and 28 px inline (44 px on touch screens), with a two-pixel accent focus ring.
  • Your responsibility: do not rely on mask to keep a value secret, and translate the labels with the page.

Release details#

On this page
Integration
  • Local interaction
  • Requires client-side JavaScript to be interactive
  • 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.