An unopinionated visual primitive. A plain block by default that gains visual treatment via attributes.
backgroundApplies a background color from the design-system token palette. When unset, the surface is transparent.
backgroundGradientbackground-gradientApplies a gradient fade across the background.
Direction and colors are customizable via CSS custom properties.
borderRenders a visible border around the surface, optionally in a themed color.
borderGradientborder-gradientRenders the border as a gradient using the color set by the border prop. Takes precedence over a solid border when both are set.
Requires border to be set to produce a colored gradient. Without border, falls back to the default border color.
clickableMakes the surface interactive, responding to clicks.
Emits tctClick when clicked.
elevationGives the component a drop shadow.
0 means no shadow; higher values appear more elevated.
insetAdds internal padding applied uniformly on all sides.
Individual axes can be overridden with inset-x and inset-y.
insetXinset-xAdds horizontal padding, overriding the horizontal component of inset.
insetYinset-yAdds vertical padding, overriding the vertical component of inset.
radiusAdds corner rounding on a scale from 0 (sharp) to 5 (most rounded).
The q2-surface element exposes events that can be used to send and receive data from the component based on user interaction.
Emitted when the surface is clicked and clickable is true.
voidThe q2-surface element exposes methods that can be used to perform various actions on the component.
Triggers a named animation on the surface. If an animation is already running, cancels it before starting the new one.
animate(animation?: "bounce" | "flash" | "pulse" | "tada") => Promise<void>q2-surface renders no visual treatment unless you apply props. A bare element with no props renders as an invisible block that takes up flow space but shows no background, border, or shadow.
Useful as a layout wrapper or padding shim
<q2-surface>
<p>Useful as a layout wrapper or padding shim</p>
</q2-surface>
Add background="primary" to give it the theme's primary color.
Primary surface background color
<q2-surface background="primary">
<p>Primary surface background color</p>
</q2-surface>
inset-x and inset-y only set the padding on their respective axis, taking priority over inset. Setting an axis to none removes padding on that axis even when inset is set to a larger value. This lets you mix uniform padding with full-bleed content on one axis.
Setting inset alone applies the same padding to both axes.
Horizontal padding only
<q2-surface border="default" inset="comfortable" background="default">
<p>Horizontal padding only</p>
</q2-surface>
Setting inset-x and inset-y controls each axis independently.
Compact vertical, comfortable horizontal
<q2-surface border="default" inset-x="comfortable" inset-y="none" background="default">
<p>Compact vertical, comfortable horizontal</p>
</q2-surface>
border-gradient reads its gradient colors from the border prop. Without border, it falls back to the default border color. When both border and border-gradient are set, the gradient is applied to the border.
Colored gradient border
<q2-surface border="primary" border-gradient inset="normal" radius="2">
<p>Colored gradient border</p>
</q2-surface>
background-gradient and border-gradient are independent props and work together. Use both for a glowing-card treatment where the border fades through the border color and the fill fades through the background color.
Content
<q2-surface
border-gradient
border="default"
radius="2"
inset="normal"
>
<p>Content</p>
</q2-surface>
clickable adds role="button" and tabIndex=0 automatically. Keyboard users can Tab to focus and activate with Enter or Space at no extra cost. The component emits tctClick on activation which should be listened for instead of a native click event, so your handler catches both pointer and keyboard activations.
Click or press Enter to activate
<q2-surface id="my-surface" clickable radius="2" elevation="2" border="default" inset="normal" background="default">
<p>Click or press Enter to activate</p>
</q2-surface>
document.getElementById('my-surface').addEventListener('tctClick', () => {
console.log('surface activated');
});
animate() accepts 'bounce', 'flash', 'pulse', or 'tada' and defaults to 'pulse'. It returns a Promise that resolves when the animation ends, enabling sequential choreography. Calling it while an animation is in progress cancels the in-flight animation first, so it is safe to call repeatedly without debouncing.
// Single animation with await
await document.getElementById('my-surface').animate('bounce');
console.log('bounce complete');
// Sequential choreography across two surfaces
const surfaceA = document.getElementById('surface-a');
const surfaceB = document.getElementById('surface-b');
await surfaceA.animate('flash');
await surfaceB.animate('tada');
Surfaces are transparent by default, so nesting them adds no visual overhead. Inner surfaces act purely as padding shims, giving you fine control over where inset applies without adding extra background layers.
An outer surface provides the border and vertical inset. An inner surface adds horizontal padding only to the search bar, leaving the list full-bleed.
<q2-surface radius="2" elevation="1" border="default" inset-y="normal" background="default">
<!-- Search bar gets horizontal padding only -->
<q2-surface inset-x="normal">
<q2-input label="Search" hide-label type="search" placeholder="Search"></q2-input>
</q2-surface>
<!-- List is full-bleed - no horizontal inset -->
<q2-list bordered>
<q2-item><div slot="header">Item one</div></q2-item>
<q2-item><div slot="header">Item two</div></q2-item>
</q2-list>
</q2-surface>
Inner surfaces with inset-y create breathing room between content sections without adding a background or border.
From: bank@example.com
Dear customer, we noticed unusual activity...
<q2-surface border="default" inset="normal" radius="2" background="default">
<h3>Subject: Security Alert</h3>
<!-- Metadata block -->
<q2-surface radius="2" border="default" inset="normal">
<p>From: bank@example.com</p>
</q2-surface>
<!-- Spacer between metadata and body -->
<q2-surface inset-y="normal">
<p>Dear customer, we noticed unusual activity...</p>
</q2-surface>
<hr />
<!-- Spacer before reply section -->
<q2-surface inset-y="normal">
<h3>Response</h3>
<q2-textarea></q2-textarea>
</q2-surface>
</q2-surface>
The following CSS variables are available to override the default theme styles of the q2-surface component.
The changelog provides a detailed history of new features, improvements, and bug fixes going back to Tecton 1.30.0. If the button is disabled, it indicates there have been no detectable changes since then.