Grove UI
Glass link cards, rose/coral profile aesthetic, pill buttons, and social icons — extracted from a mobile-first link-in-bio profile page. The glassmorphism link card is the hero component.
Colour
Two-mode system. Dark (profile) = rose background with glass cards. Light (platform) = warm off-white with clean surfaces. Both modes share the rose accent.
Profile (dark)
Platform (light)
Typography
Inter for all body/UI text. Anton (free Google Fonts substitute for source commercial display font) at 60px for the profile name. Measured directly from source elements.
| Token | Value | Usage |
|---|---|---|
--font-display | 'Anton', Impact, sans-serif | Profile name h1 |
--font-body | 'Inter', system-ui | All UI text |
--size-display | 60px | h1 — measured |
--lh-display | 57px | h1 line-height — measured |
--size-base | 16px | h2 / body — measured |
--size-sm | 14px | Card text — measured |
--size-xs | 12px | Links / meta — measured |
Spacing
4px base unit. Link card vertical padding 16px, horizontal 24px. Pill button height 48px, glass link card height 64px — all measured.
Elevation
Very subtle shadows — the glass cards use near-invisible glow. Heavier shadows reserved for modals. Layered factorial shadow also detected.
Glass link card
Hover / cards
Modals
Soft depth card
Border radius
Motion
Fast 75ms transitions on link cards. Spring easing (0.34, 1.56, 0.64, 1) detected. Four named link-card animations: wobble, pop, buzz, swipe.
Click a card to trigger its animation
| Token | Value |
|---|---|
--duration-fast | 75ms — link card transition |
--duration-normal | 150ms |
--duration-slow | 300ms |
--ease-spring | cubic-bezier(0.34, 1.56, 0.64, 1) |
--transition-card | transform 75ms, box-shadow 75ms, background-color 75ms |
Profile hero
Full-width profile header with animated gradient background, profile name in chunky display type, bio line, and profile nav. On mobile this is ~70% of viewport height.
Avatar
Measured at 142×142px with 5px border-radius (near-square). White ring variant also observed.
Ambient radial-blur backdrop
<!-- Default (near-square, measured) -->
<img class="grove-avatar" src="photo.jpg" alt="Profile photo" width="142" height="142">
<!-- Circle variant -->
<img class="grove-avatar grove-avatar--circle" src="photo.jpg" alt="Profile photo" width="96" height="96">
<!-- Circle with ring -->
<img class="grove-avatar grove-avatar--circle grove-avatar--ring" ...>
<!-- Ambient glow: blurred duplicate of the hero image, radial-masked.
Place inside a position:relative parent, behind the avatar. -->
<div class="grove-ambient-blur" aria-hidden="true">
<img src="photo.jpg" alt="">
</div>
Link card
The core component. Glass rounded rectangle — rgba(255,255,255,0.10) background plus a top-to-bottom white gradient, a 1px gradient-border highlight (::before) and a subtle feTurbulence noise texture (::after, baseFrequency 0.9 / 4 octaves) on the rose page. 362px wide, min-height 64px, 32px border-radius (standard/stack); the featured/media variant uses 28px. 12px × 32px padding, 75ms transitions. Media variant at 127px tall with thumbnail. All values measured directly.
<!-- Glass link card (text) -->
<a href="https://…" class="grove-link-card">
<div class="grove-link-card__body">
<div class="grove-link-card__title">Link title</div>
<div class="grove-link-card__meta">Description</div>
</div>
<span class="grove-link-card__arrow" aria-hidden="true">→</span>
</a>
<!-- Media card (with thumbnail) -->
<a href="https://…" class="grove-link-card grove-link-card--media">
<div class="grove-link-card__body">
<div class="grove-link-card__title">Link title</div>
</div>
<img class="grove-link-card__thumbnail" src="thumb.jpg" alt="" width="100" height="94">
</a>
<!-- Stack card (left icon tile · 32px outer radius) -->
<a href="https://…" class="grove-link-card grove-link-card--stack">
<div class="grove-link-card__icon-tile" aria-hidden="true"><svg …></svg></div>
<div class="grove-link-card__body">
<div class="grove-link-card__title">Link title</div>
</div>
</a>
<!-- Featured card (aspect-video thumbnail · 28px outer radius) -->
<a href="https://…" class="grove-link-card grove-link-card--featured">
<div class="grove-link-card__head">
<div class="grove-link-card__head-thumb">
<img src="thumb.jpg" alt="Video thumbnail">
</div>
</div>
<div class="grove-link-card__body">
<div class="grove-link-card__title">Link title</div>
</div>
</a>
Carousel
A horizontally-scrolling row of cards. 14px gap, 14px inline padding, 7px block padding, hidden scrollbar, free scroll (no snap). Each item grows and never shrinks: standard items take a 43% basis, items marked data-featured="true" take 76.5% so one hero card leads the row. Drag or swipe to scroll.
<div class="grove-carousel"> <!-- Lead card: wider 76.5% basis --> <a class="grove-link-card grove-link-card--featured" data-featured="true">…</a> <!-- Following cards: 43% basis --> <a class="grove-link-card grove-link-card--featured">…</a> <a class="grove-link-card grove-link-card--featured">…</a> </div>
Pill button
Action / donate / CTA button. 48px tall, pill radius, rgba(0,0,0,0.14) bg on rose — giving a deeper rose tint on hover to rgba(0,0,0,0.22). All values measured.
<button class="grove-btn-pill">Donate</button> <button class="grove-btn-pill grove-btn-pill--sm">Follow</button> <!-- Subscribe (white bg) --> <button class="grove-btn-subscribe">🔔 Subscribe</button>
Icon button
40px glass icon button for share/action. Circular, rgba(255,255,255,0.20) background, appears in profile header.
<button class="grove-btn-icon" aria-label="Share"> <svg …></svg> </button>
Links
Platform nav links — "Templates", "Settings" etc. Simple flex row with border-bottom dividers. 16px Inter, no decoration.
Tabs
The profile's "Links / Shop" switcher. A 192px pill rail on a translucent black ground (rgba(0,0,0,0.4), darkening to 0.5 on hover) with 2px inset padding. Two 48px tab buttons split the width; a white pill slides behind the active tab (active label turns black, inactive stays white). Keyboard-navigable with arrow keys. All values measured.
<nav class="grove-tabnav" data-tabnav="demo" aria-label="Sections">
<div class="grove-tabnav__list" role="tablist">
<span class="grove-tabnav__pill" aria-hidden="true"></span>
<button class="grove-tabnav__tab" role="tab" aria-selected="true">Links</button>
<button class="grove-tabnav__tab" role="tab" aria-selected="false">Shop</button>
</div>
</nav>
<!-- components.js sizes the pill (100/N %) and slides it on click/arrow keys.
Optional: add data-tabpanel="demo" to panels to toggle them. -->
Text input
From the contact form modal. 48px tall, 16px radius, 1.5px border, rose focus ring at 20% opacity.
<input class="grove-input" type="text" placeholder="Your name"> <input class="grove-input grove-input--error" type="email" aria-invalid="true">
Select
Toggle
<label class="grove-toggle"> <input type="checkbox"> <span class="grove-toggle__track"></span> </label>
Checkbox
Slider
This component was not present in the source site.
Badges
"NEW" sticker detected with a yellow-green (#C8F44E) rotated chip. Standard rose-accent badge also present. Pill variant and outline variant included.
<span class="grove-badge">Tag</span> <span class="grove-badge grove-badge--pill">Pill</span> <span class="grove-badge grove-badge--outline">Outline</span> <span class="grove-badge grove-badge--star">NEW</span>
Card
Two card types: glass card (on rose, rgba white) and platform card (on white, border + shadow).
Stat
A 2-column grid of metric tiles (gap: 2px). Each tile is a 20px-radius rgba(0,0,0,0.05) card with a 32px icon, then the figure in Inter extrabold (800) at 36px with tracking-tight (−0.9px) and tabular-nums, and a 12px label beneath at 0.7 opacity. Measured directly — the figure is not the Anton display face.
<div class="grove-stats-grid">
<div class="grove-stat">
<svg class="grove-stat__icon" …></svg>
<span class="grove-stat__value">90M</span>
<span class="grove-stat__label">Views</span>
</div>
</div>
Code block
This component was not present in the source site.
Table
This component was not present in the source site.
Pricing
This component was not present in the source site.
Modal
Bottom-sheet style. Slides up with spring easing. White surface, 28px top radius, shadow-xl. Detected as privacy consent + subscribe modals.
<div id="modal-id" class="grove-modal-backdrop" role="dialog" aria-modal="true">
<div class="grove-modal">
<div class="grove-modal__header">
<h2 class="grove-modal__title">Title</h2>
<button class="grove-modal__close" data-modal-close aria-label="Close">✕</button>
</div>
<p class="grove-modal__body">Content</p>
<div class="grove-modal__actions">
<button class="grove-btn-pill">Confirm</button>
</div>
</div>
</div>
Gallery
The full-height "Vibes" / Reviews sheet. A centred white dialog (32px radius) with a sticky 64px header, a scrollable body of review cards (8px radius, rgba(0,0,0,0.05) tiles, 40px avatars, gold star ratings), and a bottom bar floating over a white→transparent gradient — a black 24px-radius primary action plus a 48px round icon button. Closes on overlay click, the close button, or Escape. All values measured.
<button data-gallery-open="gallery-id">Open gallery</button>
<div id="gallery-id" class="grove-gallery-overlay" hidden>
<div class="grove-gallery" role="dialog" aria-modal="true">
<div class="grove-gallery__header">
<h2 class="grove-gallery__title">Reviews</h2>
</div>
<div class="grove-gallery__body">
<!-- review cards (.grove-review + .grove-stars) -->
</div>
<div class="grove-gallery__footer">
<div class="grove-gallery__bar">
<a class="grove-gallery__primary">More reviews</a>
<button class="grove-gallery__icon-btn" data-gallery-close>✕</button>
</div>
</div>
</div>
</div>
Progress
Poll vote result bars. Rose accent fill, 8px height, pill radius.
<div class="grove-progress"> <div class="grove-progress__bar" data-progress="72"></div> </div>
Skeleton
Shimmer loading state matching the glass card and text dimensions.
Toast
This component was not present in the source site.
Empty state
This component was not present in the source site.