Summary
ButtonGlass is a pill-shaped frosted-glass button with a leading-optional / trailing-default icon (the “Upgrade” crown). Its frosted look comes from a layered treatment — a linear-gradient fill, a linear-gradient edge ring, a neutral-grey inner shadow (the glass rim/top highlight), and a very subtle drop shadow. It ships with two states (default / active) that share identical geometry; only the gradient treatment changes, and state is driven by application logic, not CSS :hover.
Anatomy
- 1ButtonGlass — pill container. Border weight 1, radius 9999, drop shadow
0/2/8 #000000 @4%. - 2iconStart — leading icon slot (size S). Off by default.
- 3text — label. Colour text-primary #2A2A2D, centred, style body-xs-bold.
- 4iconEnd — trailing icon (the Upgrade crown, size S). On by default.
[iconStart] · text · [iconEnd], horizontal, centred. Padding 10 (top/bottom) × 16 (left/right), 4px gap.Properties / API
| Property | Type / values | Default | Description |
|---|---|---|---|
state | default · active | default | Interactive state, driven by app logic. Both states share identical geometry; only the gradient treatment differs. |
iconEnd | boolean | true | Toggles the trailing icon (the Upgrade crown). |
iconStart | boolean | false | Toggles the leading icon slot. |
text | text / swap | “Upgrade” | Label text; recolourable, defaults to text-primary. |
Variants
state = default— glass fill bg-glass (#F7F7F8 @70%) → bg-gradient-primary-end (transparent). Edge ring white @0.48.state = active— fill switches to bg-gradient-secondary-start (#FFFFFF) → …-end (#FFFFFF @0%). Edge ring white @0.65.
Size is identical for both: ~103 × 38–40px.
States
Two states exposed as variants: default and active. On web the interaction model maps :hover → active look and :active (pressed) → default look.
Sizes
Single size. Pill width hugs content (≈103px at “Upgrade”), height 40 (artwork 38). Icons 16×16 (size S). No size scale.
Effects / shadow stack
Four layered effects build the frosted look. Get these right or the glass reads as a flat button.
| # | Effect | Light | Dark |
|---|---|---|---|
| 1 | Glass fill (linear gradient, ≈234°) | bg-glass #F7F7F8 @70% → transparent #F7F7F800 | #141415 @70% → transparent |
| 2 | Edge ring (1px gradient stroke, 25°, 3 stops) | white 0.48 @0/100%, rgba(105,106,109,.04) @62% | (same structure) |
| 3 | Inner shadow (glass rim — structural) | colour neutral-600 #696A6D, offset (0, 0.5), blur 1, spread 0 | same neutral-600 → grey rim |
| 4 | Drop shadow (shadow-bevel, subtle) | offset 0/2, blur 8, spread 0, #0000000A (black @4%) | transparent drop + white-16% inset |
#00141415, not #00000000) to avoid a dark fringe · the 25° edge ring must use real-pixel geometry, not a unit/corner gradient · don’t use 50% radius — use 9999 · don’t remove the inner shadow or amplify the drop shadow.Breakpoints
Not applicable — ButtonGlass has no breakpoint axis (only state). The md = 1 token in the defs is the 1px border-weight, not a breakpoint.
Design tokens
| Token | Value |
|---|---|
| text-primary | #2A2A2D |
| body-xs-bold | Inter Semi Bold, 12/18, weight 600 |
| bg-glass | #F7F7F8 @70% (light) / #141415 @70% (dark) |
| bg-gradient-primary-end | #F7F7F800 (transparent) |
| bg-gradient-secondary-start (active) | #FFFFFF |
| bg-gradient-secondary-end (active) | #FFFFFF00 |
| neutral-600 | #696A6D (inner-shadow rim) |
| shadow-bevel | drop #0000000A (black @4%) 0/2/8 |
| radius border-radius-full · border-width 1 | 9999 · 1 |
| spacing | gap 4 · v-pad 10 · h-pad 16 |
CornerRadius=20 · SwiftUI radius ≈ Figma blur ÷ 2 · Android must use a glass inner-shadow modifier, not an inset draw-shadow.Accessibility
Critical (blockers)
- No focus state (WCAG 2.4.7 / 2.4.11) — add
state=focuswith a visible ring ≥3:1; desktop also needsstate=hover. - Glass contrast not verifiable (WCAG 1.4.3) — verify label ≥4.5:1 against worst-case content behind the blur; add a reduce-transparency solid fallback (desktop), check bright photo backgrounds (mobile).
Serious
- Missing real hover / pressed / disabled variants (4.1.2) — distinct without relying on colour alone.
- Touch target 38px below platform minimums — expand tappable area to ≥44×44pt (iOS) / 48×48dp (Android) via padding.
- No handoff a11y annotations — accessible name “Upgrade”, role button, decorative trailing icon, state→variant mapping.
Moderate
- 12px text — confirm Dynamic Type /
spscaling and reflow at 200% zoom. - Trailing crown icon must be decorative (
aria-hidden/accessibilityHidden/importantForAccessibility=no). - No access key / mnemonic for Windows/GTK (e.g. Alt+U).
- Result announcements via a polite live region; honour
prefers-reduced-motion.
Usage
✓ Do
- Use radius token
border-radius-full/9999for the pill. - Keep both fill and stroke as linear gradients.
- Keep the inner shadow — it renders the glass rim and is structural.
✕ Don't
- Don’t use
50%radius (ovals on non-square bounds). - Don’t replace the gradient with a solid background.
- Don’t remove the inner shadow or amplify the drop shadow.
Implementation status
Implemented across platforms (parity with v1.0 + the a11y fixes to be verified):
Changelog
- v1.030.6.2026
- Added a subtle inner shadow for better light-mode visibility.
- Initial release.
- v0.918.6.2026
- Initial draft.
Ticket scaffold
Title: ButtonGlass v1.0 — frosted glass button + a11y states [PLATFORM]
Acceptance criteria
- Pill (radius 9999), label + trailing crown icon;
iconStart/iconEnd/textprops. - Four-layer effect stack reproduced (gradient fill, 25° gradient edge ring, neutral-600 inner-shadow rim, subtle drop shadow), light + dark.
state=default/activeuse the correct fill tokens.- Add focus + hover + pressed + disabled states (a11y blocker) distinct without colour alone.
- Tap target ≥44×44pt / 48×48dp via padding; label contrast ≥4.5:1 with a reduce-transparency fallback.
- Trailing icon decorative; accessible name “Upgrade”; honour reduced-motion.
Jira ticket template
Jira wiki-markup. Copy into a new issue, replace every [PLACEHOLDER], and duplicate one per platform.
h2. [PLATFORM] · ButtonGlass v1.0 — frosted glass pill + accessibility states
*Project / Epic* : [EPIC-KEY] Aurora AppKit · Supernova
*Issue type* : Story
*Components* : ButtonGlass · [PLATFORM]
*Labels* : aurora-appkit, supernova, design-system, button-glass, accessibility
*Priority* : [High]
*Story points* : [estimate]
*Fix version* : ButtonGlass v1.0
*Sprint* : [sprint]
*Assignee* : [assignee]
h3. Background
A glass-morphism pill button (label + trailing "Upgrade" crown). Depth comes from a four-layer treatment. NOTE: the design Accessibility Review is currently "NOT READY FOR DEV" — this ticket must also deliver the missing interaction/focus states.
h3. Objective
Implement ButtonGlass v1.0 on [PLATFORM]: the frosted pill with its effect stack AND real focus / hover / pressed / disabled states.
h3. Design source
* Figma — "Aurora AppKit · Components" -> ButtonGlass page (Specification, Developer Handover, Accessibility Review, Changelog frames)
* Handover spec — supernova-handover/buttonglass.html
* [paste the platform-specific Figma frame link]
h3. Spec (platform-agnostic — map to [PLATFORM] idioms)
* Properties : {{state}} (default | active) · {{iconStart}} (default false) · {{iconEnd}} (default true, the crown) · {{text}} (default "Upgrade")
* Single size : pill, content-hug width, ~40 tall, icons 16x16, radius {{border-radius-full}} 9999 (never 50%)
* Effect stack (all four required) :
*# Glass fill — linear gradient {{bg-glass}} (#F7F7F8 at 70%) -> transparent {{bg-gradient-primary-end}} (keep the transparent stop the SAME rgb as the glass colour to avoid a dark fringe)
*# Edge ring — 1px linear-gradient stroke, ~25deg, white 0.48 (default) / 0.65 (active) at the ends, neutral-600 at 4% mid
*# Inner shadow — {{neutral-600}} #696A6D, offset 0/0.5, blur 1 (the glass rim; structural — do not remove)
*# Drop shadow — {{shadow-bevel}} 0/2/8 black at 4% (subtle; do not amplify)
* active swaps fill to {{bg-gradient-secondary-start}} (#FFFFFF) -> {{bg-gradient-secondary-end}}
* Typography {{body-xs-bold}} · padding 10 (v) x 16 (h) · gap 4
h3. Scope
In scope : pill + four-layer effect stack + {{state}}/{{icon*}}/{{text}} + the new focus/hover/pressed/disabled states on [PLATFORM].
Out of scope : other platforms.
h3. Dependencies / blockers
* BLOCKER: design must confirm the focus-ring colour token + hover/pressed/disabled treatments (Accessibility Review open). Build to the agreed spec.
h3. Acceptance criteria
# Renders the four-layer effect stack so it reads as glass in light and dark (no dark fringe on the transparent stop).
# {{state}} default/active use the correct fill + edge-ring tokens.
# Adds visible focus (ring contrast at least 3:1), hover, pressed and disabled — distinct without relying on colour alone.
# Touch target at least 44x44 pt (iOS) / 48x48 dp (Android) via padding; visual height may stay ~40.
# Label contrast at least 4.5:1 against worst-case content behind the blur; provide a reduce-transparency solid fallback.
# Trailing crown is decorative (hidden from assistive tech); accessible name is the label ("Upgrade"); honour reduced-motion.
h3. Definition of Done
* Matches Figma (visual diff) in light and dark; effect values per spec
* All four interaction states implemented and verified with keyboard + screen reader
* Tokens only (no hard-coded colours); radius full (not 50%)
* Tests updated; reviewed and merged; version v1.0; changelog updated
h3. QA / test notes
* Keyboard focus visible and ordered; pressed/disabled behave correctly
* Test over busy/photo backgrounds and with reduce-transparency / reduced-motion enabled