Palettes
Palettes for Ferrule, a React Native UI library for iOS, Android and web. Seven inks, or your own from one color.
| Name | Character | Light primary | Dark primary |
|---|---|---|---|
verdigris (default) | Patina green | #0A6B5E | #4FD1B5 |
cobalt | Deep blue | #1F4FD1 | #8DB0FF |
madder | Crimson | #A3123F | #FF8FAE |
vermilion | Orange red | #AB370A | #FF9A6B |
saffron | Ochre to gold | #875800 | #F2B632 |
plum | Violet | #6D28A8 | #C9A2FF |
graphite | Near neutral | #2B2F33 | #E6E8EA |
Every color derives from the ink, including surfaces and borders. Components read only semantic tokens, so a copied component needs no change. ferrule check fails on a raw color in your components directory.
madder and vermilion sit near danger, and verdigris near success. The state colors stay their own hue and every state carries a label, but check the pairing if your product leans on them.
Your own palette
Give one brand color per mode, as #RRGGBB. Built-in palettes come from the same function, so yours gets the same derivation: every surface, border, tone and state. Pass the result to createTheme, or to FerruleProvider to swap it at runtime or for one section:
import { auditPalette, createPalette, createTheme, FerruleProvider } from '@leoanimesh/ferrule';
const palette = createPalette({ light: '#7A1F5C', dark: '#F2B5DC' });
// In ferrule.theme.ts: `ferrule init` writes a theme with a palette name, and this replaces the name.
export const theme = createTheme({ palette });
// Or at runtime. `palette` replaces the theme's colors, including any `colors` overrides.
<FerruleProvider palette={palette}>...</FerruleProvider>;
// In a test: every text and interactive state the components draw.
for (const mode of ['light', 'dark'] as const) {
expect(auditPalette(palette[mode]).filter((c) => c.ratio < c.min)).toEqual([]);
}It reaches every component on iOS, Android and web, and the @leoanimesh/ferrule-native views that use theme colors. To change a few colors, use colors on top of a palette. A hand-built Palette must set every color in both modes. Anything else throws a clear error: a color that isn't #RRGGBB, a missing mode, an unknown name, or an incomplete palette.
What contrast is guaranteed
auditPalette checks the text and control pairs the components draw at their tokens, including pressed and hover fills: 4.5:1 for text, and 3:1 for control edges and focus. It does not check your own colors on images or gradients, or colors set with style or slots.
| You used | Contrast |
|---|---|
| A built-in palette | Passes auditPalette in light and dark, tested for each one |
createPalette | Passes too, tested on 1,000 random colors. Any color that would miss is darkened or lightened until it passes, so read back palette.light.primary if the exact hex matters |
colors overrides, or a hand-built Palette | Not guaranteed. Nothing moves your color, so run auditPalette on the result. It takes #RRGGBB only, and throws on anything else rather than passing it |
This is a check of color pairs, not an accessibility certification.