Figma file: [Link to published Figma file — to be added when file is published]
All reusable UI components for the Linkora SocialFi web application. This document is the canonical reference for component usage, variants, and design tokens.
| Section | Contents |
|---|---|
| Design tokens | Color, type, spacing, and shadow variables |
| Atoms | Button, Input, Textarea, Badge, Avatar, Spinner, Icon |
| Molecules | Post card, Profile card, Pool card, Transaction status, Toast, Modal shell |
| Organisms | Feed list, Navigation bar, Wallet connect header, Tip modal, Compose modal |
| Dark mode | Token overrides for data-theme="dark" |
| Do/Don't examples | Usage guidance per component |
All tokens are defined as CSS custom properties in tokens.css and as a JSON object in tokens.json. Reference them exclusively — never hard-code hex values in component code.
| Token | Value | Usage |
|---|---|---|
--color-primary |
#7C3AED |
Primary CTA, active states, links |
--color-primary-hover |
#6D28D9 |
Primary hover state |
--color-primary-light |
#EDE9FE |
Primary tint background |
--color-secondary |
#06B6D4 |
Secondary actions, creator token badges |
--color-accent |
#F59E0B |
Tip / reward highlights |
--color-success |
#10B981 |
Confirmed state, success toasts |
--color-error |
#EF4444 |
Error state, delete actions |
--color-warning |
#F59E0B |
Warning banners |
--color-bg |
#FFFFFF |
Card and page backgrounds |
--color-surface-1 |
#F9FAFB |
Page background |
--color-surface-2 |
#F3F4F6 |
Input backgrounds, skeleton base |
--color-border |
#E5E7EB |
Borders at rest |
--color-border-strong |
#D1D5DB |
Focused borders |
--color-text-primary |
#111827 |
Body text |
--color-text-secondary |
#6B7280 |
Timestamps, metadata |
--color-text-disabled |
#9CA3AF |
Disabled labels |
| Token | Value | Usage |
|---|---|---|
--font-sans |
Inter, system-ui | All UI text |
--font-mono |
JetBrains Mono | Addresses, transaction hashes |
--text-xs |
0.75 rem | Tags, hints |
--text-sm |
0.875 rem | Metadata, timestamps |
--text-base |
1 rem | Body text |
--text-lg |
1.125 rem | Card titles |
--text-xl |
1.25 rem | Section headings |
--text-2xl |
1.5 rem | Page headings |
--font-regular |
400 | Body |
--font-medium |
500 | Emphasis |
--font-semibold |
600 | Labels, buttons |
--font-bold |
700 | Headings |
| Token | Value |
|---|---|
--spacing-xs |
0.25 rem |
--spacing-sm |
0.5 rem |
--spacing-md |
1 rem |
--spacing-lg |
1.5 rem |
--spacing-xl |
2 rem |
| Name | CSS value | Usage |
|---|---|---|
shadow-sm |
0 1px 2px rgba(0,0,0,0.05) |
Cards at rest |
shadow-md |
0 4px 8px rgba(0,0,0,0.08) |
Elevated cards, dropdowns |
shadow-lg |
0 20px 40px rgba(0,0,0,0.15) |
Modals |
Four variants, four states each. All buttons have min-height: 44px (touch target).
| Variant | Background | Text | Border | Usage |
|---|---|---|---|---|
| primary | --color-primary |
white | — | Main CTA |
| secondary | transparent | --color-primary |
--color-primary |
Secondary action |
| ghost | transparent | --color-text-primary |
--color-border |
Tertiary, cancel |
| danger | --color-error |
white | — | Destructive actions |
| State | Visual |
|---|---|
| idle | Default colors above |
| hover | 8% darker background, cursor: pointer |
| disabled | 50% opacity, cursor: not-allowed, no pointer events |
| loading | Label replaced by <Spinner />, disabled |
<!-- Primary button -->
<button class="btn btn--primary">Follow</button>
<!-- Loading state -->
<button class="btn btn--primary" disabled aria-busy="true">
<span class="spinner" aria-hidden="true"></span>
<span class="sr-only">Loading…</span>
</button>Single-line text field.
| State | Border color | Description |
|---|---|---|
| rest | --color-border |
Default |
| focus | --color-border-strong |
2px outline in --color-primary-light |
| error | --color-error |
Red border + error message below |
| disabled | --color-border |
--color-surface-2 background, 60% opacity |
Always pair with a <label> and optional hint text. Error messages go in a role="alert" span linked via aria-describedby.
Same states as Input. resize: vertical only. Minimum 3 rows.
Pill-shaped label for metadata.
| Variant | Background | Text | Usage |
|---|---|---|---|
| default | --color-surface-2 |
--color-text-secondary |
Generic tags |
| creator | --color-primary-light |
--color-primary |
Creator token address |
| testnet | #DBEAFE |
#1E40AF |
Network indicator |
| mainnet | #D1FAE5 |
#065F46 |
Network indicator |
Circular user avatar. Uses an SVG placeholder (person silhouette on --color-surface-2) when no image is available.
| Size | Diameter | Usage |
|---|---|---|
sm |
32 px | Comment threads |
md |
40 px | Post card |
lg |
48 px | Profile card |
xl |
72 px | Profile page header |
Animated CSS ring. Inherits currentColor for the active arc.
<span class="spinner" role="status" aria-label="Loading"></span>Use Lucide icons at 20×20 px for inline icons and 24×24 px for standalone. Stroke width: 2 px. Color inherits from parent.
Recommended icons:
| Use case | Lucide name |
|---|---|
| Like | Heart / HeartOff |
| Tip | Gem |
| Follow | UserPlus |
| Unfollow | UserMinus |
| Share | Link |
| Delete | Trash2 |
| Copy | Copy |
| Feed | LayoutList |
| Explore | Compass |
| Pool | Layers |
| Profile | User |
Compact summary card used in feeds and profile post lists.
Anatomy:
- Author row — avatar (md), username (bold), timestamp (text-sm, secondary)
- Content — up to 280 chars, no truncation in detail view, 3-line clamp in feed
- Action row — Like button, Tip badge, Tip button
States: has_liked toggles the heart icon from outline to filled.
Linked to: PostCard.tsx
Compact card for search results and explore page.
Anatomy:
- Avatar (lg)
- Username + follower count
- Follow / Following button
Linked to: ProfileCard.tsx
Displays a community pool's balance, admin threshold, and deposit/withdraw entry points.
Anatomy:
- Pool name (badge)
- Token + balance
- Threshold indicator (e.g., "2 of 3 admins required")
- Deposit and Withdraw buttons
Inline feedback shown during and after on-chain actions.
| Step | Icon | Text | CTA |
|---|---|---|---|
| idle | — | Amount summary | Confirm button |
| awaiting signature | Spinner | "Check Freighter…" | — |
| submitting | Spinner | "Submitting…" | — |
| confirmed | ✅ | "Confirmed", truncated tx hash | Link to Stellar Expert |
| failed | ❌ | Human-readable error | Retry button |
Non-blocking feedback, auto-dismisses after 5 s.
| Variant | Left border color | Icon |
|---|---|---|
| success | --color-success |
✅ |
| error | --color-error |
❌ |
| info | --color-primary |
ℹ️ |
Positioned: top-right on desktop, bottom-center on mobile. Includes a close (×) button.
Base container for all modal dialogs.
- Fixed
position: fixed; inset: 0backdrop,rgba(0,0,0,0.45)overlay - Modal card:
border-radius: 16px,shadow-lg, max-width 420 px, responsive - Trap focus within modal while open
- Close on backdrop click and
Escapekey
Vertical list of post cards with a skeleton-loader placeholder while fetching.
- Skeleton: 3 animated shimmer cards (see
skeleton-shimmerkeyframe inglobals.css) - Empty state: centered illustration + "No posts yet" copy
Desktop: fixed left sidebar, 240 px wide.
- Logo top-left
- Nav items: Feed, Explore, Pools, My Profile
- Active state:
--color-primary-lightbackground + left border in--color-primary
Mobile: fixed bottom tab bar, 56 px tall.
- 4 icon + label items
- Active:
--color-primaryfilled icon
Fixed header, 64 px tall.
| State | Right slot content |
|---|---|
| disconnected | "Connect Wallet" (secondary button) |
| connected | Avatar (sm) + truncated address (G…K2) + chevron-down |
Full tip flow inside the Modal shell.
- Token selector (dropdown — XLM or supported assets)
- Amount input with live fee breakdown:
- Gross tip amount
- Protocol fee (fee_bps %)
- Net to author = gross − fee
- Transaction status component (see above)
- Mobile variant: bottom sheet with drag handle
New post form inside the Modal shell.
- Author avatar (md) + character counter (0 / 280)
- Textarea (auto-grow, 3–6 rows)
- Post button (primary, disabled until content ≥ 1 char)
Dark mode activates when data-theme="dark" is set on <html>. Override the following tokens:
| Token | Dark value |
|---|---|
--color-bg |
#0D0D12 |
--color-surface-1 |
#1A1A23 |
--color-surface-2 |
#252530 |
--color-border |
#2E2E3A |
--color-border-strong |
#3E3E4E |
--color-text-primary |
#F5F5F7 |
--color-text-secondary |
#8E8E93 |
--color-text-disabled |
#4B5563 |
All other brand and semantic tokens remain the same in dark mode to preserve contrast ratios.
✅ Do — Use primary for the single most important action per view.
<button class="btn btn--primary">Confirm tip</button>❌ Don't — Stack multiple primary buttons side by side.
<!-- Bad: two primary buttons compete for attention -->
<button class="btn btn--primary">Like</button>
<button class="btn btn--primary">Tip</button>✅ Do — Always truncate with a copy button. Never ask users to read a full address.
GABC…1234 ⎘
❌ Don't — Display a raw 56-character address inline in body text.
✅ Do — Place the error message directly below the offending field, linked via aria-describedby.
❌ Don't — Show errors only in a banner at the top of the form.
✅ Do — Replace the button label with a <Spinner /> and set disabled during async operations.
❌ Don't — Leave the button active or show a full-page spinner for a single-field action.
✅ Do — Always show the fee breakdown before the user confirms.
❌ Don't — Deduct the fee silently without showing the net-to-author amount.
Last updated by issue #163 — initial component library specification.