Skip to content
Foundationsstable

Icons

Lucide, outline, 2px stroke, coloured by the text around it. One library — never mix in emoji or another icon set for UI.

The set

lucide-vue-next. Import each icon by name; tree-shaking keeps only what you use. 106 different icons are imported across 72 files — these are the most used, ordered by how many files import them.

  • ArrowRightForward CTA, “see all”25 files
  • CheckDone, selected, included24 files
  • MapPinVenue, city24 files
  • UsersWho’s going, community23 files
  • XClose, remove16 files
  • CalendarDate15 files
  • PlusAdd to plan, create14 files
  • TicketTickets, passes12 files
  • SparklesNew, recommended10 files
  • ExternalLinkLeaves WeDance10 files
  • ArrowLeftBack10 files
  • Loader2Loading (with animate-spin)10 files
  • UtensilsCrossedFood, catering9 files
  • ChevronDownExpand, dropdown8 files
  • GlobeWebsite, language8 files
  • MessageCircleChat, ask locals7 files
  • HeartSave, favourite7 files
  • InstagramInstagram link6 files
  • GiftFree, perks6 files
  • SearchSearch5 files
  • RotateCcwRetry, reset5 files
  • Share2Share5 files
  • YoutubeYouTube link5 files
  • CalendarDaysDate range, schedule5 files
  • CompassExplore4 files
  • ChevronRightRow drill-in4 files
  • SaveSave form4 files
  • PlayPlay video4 files
  • StarRating, featured4 files
  • UserPlusInvite, follow4 files
  • MusicStyle, DJ4 files
  • VideoVideo4 files
  • EyePreview, views4 files
  • ArrowUpRightOpen detail3 files

Before adding a new icon, check this grid for one that already means the same thing. Browse the full set at lucide.dev/icons.

Sizes

Match the icon to the text it sits with. Always set width and height together (or size-*).

w-3 h-3Inside eyebrows, badges and 10–11px meta text
w-3.5 h-3.5With text-xs / text-sm — chips, small buttons, meta rows
w-4 h-4Default. Body text, buttons, inputs, list items
w-5 h-5Icon-only buttons, card headers, nav
w-6 h-6Feature tiles, section icons
w-8 h-8Empty states, big feature icons

Stroke and colour

Stroke: 2px, always

Lucide’s default. All 476 icon uses in the app keep it — don’t pass stroke-width. Thinner strokes disappear on cream at small sizes.

Colour: inherit the text

Icons draw in currentColor. Colour the parent, not the icon. Use a status colour only when the icon is the status.

MunichSee allSaved

Icon + text

inline-flex items-center with a gap — never a margin on the icon, never vertical-align hacks.

Munich New this week
vue
<script setup lang="ts">
import { MapPin } from 'lucide-vue-next'
</script>

<span class="inline-flex items-center gap-1.5 text-sm text-muted-foreground">
  <MapPin class="w-4 h-4 shrink-0" aria-hidden="true" />
  Munich
</span>
  • Gap: gap-1 with 12px icons, gap-1.5 with 14–16px, gap-2 in buttons.
  • Icon first for a label (pin + “Munich”), icon last for direction (“See all” + arrow).
  • Add shrink-0 when the text can wrap, so the icon keeps its size.
  • For multi-line text, align to the first line: flex items-start + mt-0.5 on the icon.

Icon-only buttons

Allowed for universal actions — close, share, menu, back. Everything else gets a label.

vue
<button type="button" aria-label="Close" class="p-2 rounded-full hover:bg-accent">
  <X class="w-5 h-5" aria-hidden="true" />
</button>
<button aria-label="Close">
  <X aria-hidden="true" />
</button>

Do — label the action and hide the icon from screen readers.

<button>
  <X />
</button>

Don't — ship an icon-only button with no aria-label — screen readers announce “button”.

Accessibility

  • Decorative icons next to text get aria-hidden="true" — the text already says it.
  • Icon-only buttons and links need an aria-label that names the action (“Close”, “Share festival”), not the icon (“X”).
  • Give icon-only buttons at least a 40×40px hit area: p-2 around a w-5 icon, or p-2.5 around w-4.
  • Never rely on an icon alone to carry status — pair Check / X with a word or colour + text.
  • Spinners (Loader2 animate-spin) sit next to a visible “Saving…” label or inside a button with aria-busy="true".