Icons
VitNode's icon system - lucide-react icons, a size scale, auto-sized icons in buttons and badges, icons loaded by name, brand logos and accessible icon-only buttons.
VitNode uses lucide-react for icons: over 1,500 outlined icons with one stroke style. Components size them for you, so most of the time you just drop one in.
Size scale
size-4 is the default. It matches text-sm, the size of buttons, menus and
inputs. Go up for navigation and empty states, down for badges.
Icons in components
Buttons, badges and input groups size any child svg that has no size-*
class. Add data-icon="inline-start" or "inline-end" so the button trims
its padding on that side.
<Button>
<PlusIcon data-icon="inline-start" />
New post
</Button>Stroke
Keep the default strokeWidth={2}. It sits right next to font-medium text.
Thinner or bolder strokes are for big hero icons, not your toolbar.
Icons by name
Store an icon as a string and render it with DynamicIcon. Each icon loads on
its own, and icons rendered on the server arrive with the page, so nothing pops
in. An unknown name renders nothing.
import { DynamicIcon } from '@vitnode/core/components/ui/dynamic-icon'
<DynamicIcon className="size-5" name="shield-check" />Letting users pick one? The Emoji & Icon Picker returns exactly this name.
Do and don't
- Lucide hides icons from screen readers by default (
aria-hidden="true"), so an icon next to text needs nothing extra. - Icon-only buttons need an
aria-label. TypeScript asks for it onsize="icon". - Lucide has no brand logos. Use an inline SVG with
fill="currentColor"andaria-hidden.
Related
Motion
VitNode's motion rules - instant press feedback, the ease-fluid curve, interruptible transitions, critically damped springs, trigger-aware origins and reduced motion, shown live.
Accessibility
How VitNode UI stays usable for everyone - keyboard focus rings, roomy touch targets, WCAG contrast, screen reader text, live announcements and reduced motion, shown live.