← All proposals

Agentic Split Button

A separate component, `AgenticSplitButton`, a copy of the PDS v2 Split Button, so the engineer's port is mostly a copy:

Net-new componentOpen in Storybook

proposals/AgenticSplitButton/agentic-split-button.css135 lines · View on GitHub
.pds-split-button {
	background-color: var(--pds-color-button-primary-background-default);
	border-radius: var(--pds-border-radius-button);
	display: inline-flex;
	gap: var(--pds-border-width-default);
}

/* Remove border radius where buttons meet */
.pds-split-button button.pds-split-button__primary-action,
.pds-split-button .pds-split-button__primary-action a {
	border-bottom-right-radius: 0;
	border-top-right-radius: 0;
	padding-inline-end: var(--pds-spacing-s);
}

.pds-split-button button.pds-menu-button__trigger {
	border-bottom-left-radius: 0;
	border-top-left-radius: 0;
	padding-inline-start: var(--pds-spacing-s);
	position: relative;
}

/* Full-height separator centered in gap between buttons */
.pds-split-button button.pds-menu-button__trigger::before {
	background-color: var(--pds-color-surface-default);
	content: '';
	height: calc(100% + 2 * var(--pds-border-width-default));
	left: calc(var(--pds-border-width-default) * -1);
	position: absolute;
	top: calc(-1 * var(--pds-border-width-default));
	transform: translateX(-100%);
	width: var(--pds-border-width-default);
}

/* Tally and status dot are not supported on split buttons */
.pds-split-button .pds-button__tally,
.pds-split-button .pds-status-dot {
	display: none;
}

/* Secondary variant */
.pds-split-button--secondary {
	background-color: var(--pds-color-button-secondary-background-default);
	border: var(--pds-border-width-default) solid
		var(--pds-color-button-secondary-border);
}

/* Secondary variant - remove all button borders since wrapper has border */
.pds-split-button--secondary button.pds-split-button__primary-action,
.pds-split-button--secondary .pds-split-button__primary-action a {
	border: 0;
}

.pds-split-button--secondary button.pds-menu-button__trigger {
	border: 0;
}

/* Secondary variant separator - adjust position due to wrapper border */
.pds-split-button--secondary button.pds-menu-button__trigger::before {
	background-color: var(--pds-color-button-secondary-border);
	transform: translateX(0);
}

/* Agentic modifier (primary and secondary): the AI gradient as the border
   around the whole control, as on the agentic Button. A gradient cannot go in
   border-color, so the wrapper's border is transparent, the gradient is
   painted on its border-box and the fill layers sit on the padding-box above
   it. The secondary background token is translucent, so an opaque surface
   layer keeps the gradient from showing through the middle. The halves lose
   their own borders, and are 2 border widths shorter so the control matches
   the height of a standalone Button. */
.pds-agentic-split-button {
	--split-button-fill: var(--pds-color-button-secondary-background-default);

	background-clip: padding-box, padding-box, border-box;
	background-color: transparent;
	background-image:
		linear-gradient(var(--split-button-fill), var(--split-button-fill)),
		linear-gradient(
			var(--pds-color-surface-default),
			var(--pds-color-surface-default)
		),
		var(--pds-gradient-ai-active);
	background-origin: border-box;
	border: var(--pds-border-width-default) solid transparent;
}

.pds-agentic-split-button button.pds-split-button__primary-action,
.pds-agentic-split-button button.pds-menu-button__trigger {
	border: 0;
}

.pds-agentic-split-button .pds-button--s {
	--button-height: calc(
		var(--pds-spacing-button-height-s) - 2 * var(--pds-border-width-default)
	);
}

.pds-agentic-split-button .pds-button--m {
	--button-height: calc(
		var(--pds-spacing-button-height-m) - 2 * var(--pds-border-width-default)
	);
}

/* Animated (agentic only): the gradient layer (the third) is twice the
   control's width and repeats, and slides by one tile so the loop is
   seamless. The fill layers stay put. Off for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
	.pds-agentic-split-button.pds-agentic-split-button--animated {
		animation: agentic-split-button-shimmer
			var(--pds-animation-duration-ai-shimmer) linear infinite;
		background-repeat: no-repeat, no-repeat, repeat-x;
		background-size:
			100% 100%,
			100% 100%,
			200% 100%;
	}
}

@keyframes agentic-split-button-shimmer {
	from {
		background-position:
			0 0,
			0 0,
			0 0;
	}

	to {
		background-position:
			0 0,
			0 0,
			-200% 0;
	}
}