← All proposals

LabeledDivider

Net-new LabeledDivider: a horizontal rule with a pill-shaped label centered on it.

Net-new componentOpen in Storybook

proposals/LabeledDivider/labeled-divider.css110 lines · View on GitHub
.pds-labeled-divider {
	--labeled-divider-label-background: transparent;
	--labeled-divider-label-border: var(--pds-color-border-separator);
	--labeled-divider-label-color: var(--pds-color-foreground-default);
	--labeled-divider-line-color: var(--pds-color-border-separator);
	--labeled-divider-padding-block: var(--pds-spacing-3xs);
	--labeled-divider-padding-inline: var(--pds-spacing-s);

	align-items: center;
	display: flex;
	gap: var(--pds-spacing-s);
	width: 100%;
}

/* Sizes are named after the type tokens: the text takes the matching size and
   the padding shrinks for xs. */
.pds-labeled-divider--xs {
	--labeled-divider-padding-block: var(--pds-spacing-4xs);
	--labeled-divider-padding-inline: var(--pds-spacing-xs);
}

/* Agentic: the AI gradient as the label border. A gradient cannot go in
   border-color, so the border is transparent, the gradient is painted on the
   border-box and an opaque surface layer covers the padding-box (same pattern
   as the agentic Button). */
.pds-labeled-divider--agentic {
	--labeled-divider-label-border: transparent;
}

/* The rule on each side takes the same gradient, with the shimmer band on top. */
.pds-labeled-divider--agentic .pds-labeled-divider__line {
	background-image:
		var(--pds-gradient-ai-shimmer), var(--pds-gradient-ai-active);
	background-size:
		250% 100%,
		auto;
}

/* Shimmer: the PDS shimmer band is an extra background layer on the border,
   and the lines, swept across with the PDS keyframes. The layer is
   under the label's surface fill, so it shows on the border only, not on the
   text. Off for reduced motion. */
.pds-labeled-divider--agentic .pds-labeled-divider__label,
.pds-labeled-divider--agentic .pds-labeled-divider__line {
	animation: pds-ai-shimmer-sweep var(--pds-animation-duration-ai-shimmer)
		linear infinite;
}

@media (prefers-reduced-motion: reduce) {
	.pds-labeled-divider--agentic .pds-labeled-divider__label,
	.pds-labeled-divider--agentic .pds-labeled-divider__line {
		animation: none;
	}
}

.pds-labeled-divider--agentic .pds-labeled-divider__label {
	background-clip: padding-box, border-box, border-box;
	background-image:
		linear-gradient(
			var(--pds-color-surface-default),
			var(--pds-color-surface-default)
		),
		var(--pds-gradient-ai-shimmer), var(--pds-gradient-ai-active);
	background-origin: border-box;
	background-size:
		auto,
		250% 100%,
		auto;
}

.pds-labeled-divider__line {
	background-color: var(--labeled-divider-line-color);
	flex: 1 1 0;
	height: var(--pds-border-width-default);
	min-width: var(--pds-spacing-m);
}

.pds-labeled-divider__label {
	align-items: center;
	background-color: var(--labeled-divider-label-background);
	border: var(--pds-border-width-default) solid
		var(--labeled-divider-label-border);
	border-radius: var(--pds-border-radius-button);
	color: var(--labeled-divider-label-color);
	display: inline-flex;
	flex-shrink: 0;
	gap: var(--pds-spacing-2xs);
	padding-block: var(--labeled-divider-padding-block);
	padding-inline: var(--labeled-divider-padding-inline);
}

.pds-labeled-divider__icon {
	flex-shrink: 0;
}

/* The selector is the secondary Button; only its shape and gap change. */
.pds-labeled-divider__selector {
	flex-shrink: 0;
}

.pds-labeled-divider__selector .pds-button {
	gap: var(--pds-spacing-2xs);
}

/* A control swapped in for the label (the `action` slot): it keeps its own
   look and is centered on the line. */
.pds-labeled-divider__action {
	flex-shrink: 0;
}