Skip to content
Thanawi

Icons

Lucide is the only icon library. How to size, colour, label and mirror icons in Thanawi products.

Usage

Every icon comes from Lucide through the lucide-react package. Browse the full set at lucide.dev/icons and import icons by name:

import { ArrowRight } from 'lucide-react';

<Button endIcon={<ArrowRight className="rtl:-scale-x-100" />}>Continue</Button>;
  • Lucide only. No other icon library, and no hand-drawn <svg> icons. The Thanawi logo is the only exception.
  • One version. @thanawi/ui and the docs pin the same lucide-react range, so an icon looks the same everywhere.
  • Enforced. pnpm --filter docs lint fails on any other icon package or Lucide package, on deep lucide-react/… paths, on .svg imports and on inline <svg>.

Sizes

Components size their own icons with [&_svg]:size-*. Pass the icon without a size and it matches the component automatically:

Icon sizeUtilityUsed in
12pxsize-3Badge sm
14pxsize-3.5Badge md, Input error message
16pxsize-4Button and Input sm / md, icon buttons icon-sm / icon
20pxsize-5Button and Input lg, icon button icon-lg, Alert icon
12px · size-3Badge sm
Verified
14px · size-3.5Badge md · field error
Verified
16px · size-4Button / Input sm · md
20px · size-5Button / Input lg · Alert

Outside a component, give the icon a size class that matches its text:

TextIcon
text-xssize-3.5 (14px)
text-smsize-4 (16px)
text-basesize-5 (20px)

Stroke

Keep Lucide’s default stroke: strokeWidth={2} on its 24px grid. The stroke scales with the icon, so it stays in proportion: about 1.33px at 16px and 1.67px at 20px. That keeps icons close to the weight of the Cairo text beside them at every size.

Don’t pass strokeWidth or absoluteStrokeWidth. A fixed 2px stroke makes small icons heavy and fills in their detail, as the second row shows.

Default — strokeWidth 2 on a 24px grid, scales with the icon
12px · 1px
14px · 1.17px
16px · 1.33px
20px · 1.67px
24px · 2px
absoluteStrokeWidth — avoid: small icons get heavy
12px · 2px
14px · 2px
16px · 2px
20px · 2px
24px · 2px

Colour

Lucide icons draw with currentColor. Set the colour on the text with a semantic token and the icon follows. Never hard-code a color, fill or stroke.

Balancetext-content-default
Funds receivedtext-content-positive
Review pendingtext-content-warning
Payment failedtext-content-negative
text-content-info
text-content-on-primary
  • Use the status tokens (text-content-positive, -warning, -negative, -info) only when the icon carries that status.
  • On filled surfaces, the component already sets the matching content-on-* colour.

Accessibility

  • Decorative icons sit next to visible text: in startIcon / endIcon, input adornments, badges. Write nothing. lucide-react renders aria-hidden="true" on any icon without an aria-*, role or title prop. Only the icons a component draws itself (the loading spinner, the dismiss ×, the field-error icon and the alert status icon) set aria-hidden="true" explicitly.
  • Icon-only controls: label the control, not the icon. Put a localised aria-label on the button; the icon stays decorative.
  • Meaningful standalone icons carry information and have no visible text. Give them role="img" and a localised aria-label. Prefer visible text when there’s room.
  • Never rely on an icon alone to convey status. Pair it with text, as in Colors › Status colors.
<Button startIcon={<Download />}>Download statement</Button>

<Button size="icon" variant="outline" aria-label="تحديث الرصيد">
  <RefreshCw />
</Button>

<BadgeCheck role="img" aria-label="Verified" className="size-5 text-content-positive" />
Decorative: write nothing
Icon-only: label the control
Meaningful on its own: label the icon

RTL flipping

Icons that point along the reading direction flip in right-to-left layouts; everything else stays as drawn. Put rtl:-scale-x-100 on the icon itself. It follows the nearest dir. Icon names are as on lucide.dev/icons. Use the EN / AR toggle on the preview:

Flip with rtl:-scale-x-100Never flip
Horizontal and diagonal arrows: arrow-left / -right, arrow-up-rightVertical: arrow-up / -down, chevron-down, download, upload
Left/right chevrons: chevron-left / -right, chevrons-left / -rightSymmetric: plus, x, check, info, circle-alert, triangle-alert
move-left / -right, send, trending-up / -downRotation, not direction: the spinner, refresh-cw, clock
undo / redo, reply, forwardReal-world objects: search, file-text
log-in / log-out, external-linkBrand marks, including the Thanawi logo
Arrives in 2 min

Flip in RTL

arrow-right
arrow-up-right
chevron-right
move-right
send
trending-up
undo
reply
log-in
external-link

Never flip

arrow-down
chevron-down
download
plus
x
check
info
refresh-cw
clock
search
file-text
brand

A component doesn’t know which icon you pass, so flipping is your call. Button puts startIcon and endIcon on the correct side in both directions, but the glyph itself flips only if you add the class. No current component draws a directional icon itself; future ones that do (a Select chevron, a Breadcrumb separator) will flip it internally.

Icons in use

Every Lucide icon the design system and this site import today, scanned from the source at build time. Search by name or by where it’s used; hover a tile to copy its import.

  • ArrowDownLeftdocs/preview/direction-demo
  • ArrowLeftdocs/docs-pager
  • ArrowRightui/examples/alert-demo +7
  • ArrowUpRightui/examples/button-as-child
  • Checkdocs/copy-button +1
  • ChevronRightdocs/preview/direction-demo
  • CircleAlertui/alert +1
  • CircleCheckui/alert +1
  • Componentdocs/app/page
  • Copydocs/copy-button
  • CornerDownLeftdocs/search
  • Downloadui/examples/button-icons
  • FileTextui/examples/card-demo +1
  • Hashdocs/search
  • Infoui/alert +1
  • Languagesdocs/app/page
  • Layersdocs/app/page
  • LoaderCircleui/button
  • Menudocs/mobile-nav
  • Moondocs/mode-toggle
  • OctagonAlertdocs/callout
  • Plusui/examples/button-sizes
  • RefreshCwui/examples/button-loading
  • Searchui/examples/input-adornments +2
  • Sendui/examples/button-icons
  • SquareDasheddocs/preview/example-renderer
  • Sundocs/mode-toggle
  • TextAlignStartdocs/docs-toc
  • TextQuotedocs/search
  • TrendingUpui/examples/card-demo
  • TriangleAlertui/alert +1
  • Xui/alert +1