UI: light Things-3 design system — tokens, task rows, chips, home
Replaces the dark navy palette with a light grouped-background system: - theme.ts: full token set (space/radius/type/shadow) + light+dark palettes - ui.tsx: Screen/Title/SectionHeader/Card/Separator/Pill/Empty primitives - TaskRow: rounded-square checkbox carrying priority colour, inset separators - TaskListScreen: rows grouped into a card, drag lift, refined deferred footer - Icon.tsx: dependency-free geometric icon set (emoji don't take a tint) - home: large title, Next-up card, "needs attention" rows, icon tab bar - Priority/Estimate/Date chips unified on the accent-soft active state Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,11 +9,21 @@ export const PRIORITY_LABEL: Record<Task["priority"], string> = {
|
||||
4: "Low",
|
||||
};
|
||||
|
||||
// Todoist's flag mapping (urgent red → low grey), retuned to sit calmly on a
|
||||
// light canvas. `PRIORITY_TINT` is the same hue at wash strength, for the
|
||||
// selected state of a chip — a saturated fill would shout on a white card.
|
||||
export const PRIORITY_COLOR: Record<Task["priority"], `#${string}`> = {
|
||||
1: "#E05B5B",
|
||||
2: "#E0A93E",
|
||||
3: "#5B8DEF",
|
||||
4: "#6B7280",
|
||||
1: "#E5484D",
|
||||
2: "#D98324",
|
||||
3: "#2F6FED",
|
||||
4: "#9A9AA2",
|
||||
};
|
||||
|
||||
export const PRIORITY_TINT: Record<Task["priority"], `#${string}`> = {
|
||||
1: "#FDECEC",
|
||||
2: "#FBF0E2",
|
||||
3: "#E8F0FE",
|
||||
4: "#F0F0F3",
|
||||
};
|
||||
|
||||
export function formatEstimate(min: number | null): string | null {
|
||||
|
||||
@@ -1,12 +1,115 @@
|
||||
export const C = {
|
||||
bg: "#0F1115",
|
||||
surface: "#1A1D24",
|
||||
surface2: "#232733",
|
||||
border: "#2C313D",
|
||||
text: "#E6E8EC",
|
||||
muted: "#8A909C",
|
||||
accent: "#5B8DEF",
|
||||
done: "#4CAF7D",
|
||||
danger: "#E05B5B",
|
||||
warn: "#E0A93E",
|
||||
// Design tokens.
|
||||
//
|
||||
// The look is modelled on Things 3: a light, calm canvas, generous whitespace,
|
||||
// hairline inset separators instead of boxes, and exactly one accent colour
|
||||
// used sparingly. Everything visual in the app resolves through this file —
|
||||
// no component hard-codes a hex value (priority flags are the one exception,
|
||||
// and they live in `priority.ts` alongside their labels).
|
||||
|
||||
/** Named palette. `dark` is complete and ready to wire to a theme toggle. */
|
||||
const light = {
|
||||
/** Screen background behind grouped content. */
|
||||
bg: "#F7F7F9",
|
||||
/** Card / row surface that sits on `bg`. */
|
||||
surface: "#FFFFFF",
|
||||
/** Inputs, pressed states, subtle fills. */
|
||||
surface2: "#EFEFF3",
|
||||
/** Card outline. Deliberately barely-there. */
|
||||
border: "#E6E6EB",
|
||||
/** Inset hairline between rows inside a card. */
|
||||
separator: "#ECECF0",
|
||||
/** Primary reading colour. Near-black, never pure #000. */
|
||||
text: "#16161A",
|
||||
/** Supporting copy that should still be comfortably readable. */
|
||||
textSecondary: "#6E6E76",
|
||||
/** Metadata, placeholders, de-emphasised chrome. */
|
||||
muted: "#9A9AA2",
|
||||
accent: "#2F6FED",
|
||||
/** Tinted accent wash for selected chips and active states. */
|
||||
accentSoft: "#E8F0FE",
|
||||
done: "#2FA36B",
|
||||
danger: "#E5484D",
|
||||
warn: "#D98324",
|
||||
/** Text that sits on top of `accent`. */
|
||||
onAccent: "#FFFFFF",
|
||||
} as const;
|
||||
|
||||
const dark = {
|
||||
bg: "#0C0C0F",
|
||||
surface: "#17171B",
|
||||
surface2: "#212127",
|
||||
border: "#2A2A31",
|
||||
separator: "#26262C",
|
||||
text: "#F2F2F5",
|
||||
textSecondary: "#A6A6B0",
|
||||
muted: "#7A7A85",
|
||||
accent: "#4C86F0",
|
||||
accentSoft: "#1B2740",
|
||||
done: "#3FBF84",
|
||||
danger: "#F0605F",
|
||||
warn: "#E0A93E",
|
||||
onAccent: "#FFFFFF",
|
||||
} as const;
|
||||
|
||||
export const PALETTES = { light, dark } as const;
|
||||
|
||||
/**
|
||||
* Active palette. Styles are created at module scope (StyleSheet.create) so
|
||||
* this is a static choice rather than a hook — switching themes at runtime
|
||||
* would mean moving every stylesheet inside its component.
|
||||
*/
|
||||
export const C = light;
|
||||
|
||||
/** 4pt spacing grid. Screen gutter is `space.lg`. */
|
||||
export const space = {
|
||||
xs: 4,
|
||||
sm: 8,
|
||||
md: 12,
|
||||
lg: 16,
|
||||
xl: 20,
|
||||
xxl: 28,
|
||||
} as const;
|
||||
|
||||
export const radius = {
|
||||
sm: 8,
|
||||
md: 12,
|
||||
/** Cards and grouped lists. */
|
||||
lg: 16,
|
||||
/** Chips and pills. */
|
||||
pill: 999,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Type scale. `title` is the large in-content screen heading that replaces a
|
||||
* cramped nav-bar title; `row` is the task title size — 17pt, matching the
|
||||
* platform body size Things and Todoist both settle on.
|
||||
*/
|
||||
export const type = {
|
||||
title: { fontSize: 30, fontWeight: "700", letterSpacing: -0.6 },
|
||||
section: { fontSize: 13, fontWeight: "600", letterSpacing: 0.3 },
|
||||
row: { fontSize: 17, letterSpacing: -0.2 },
|
||||
body: { fontSize: 15 },
|
||||
meta: { fontSize: 13 },
|
||||
chip: { fontSize: 14, fontWeight: "500" },
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Card elevation. Kept extremely soft — a Things-like surface reads as paper,
|
||||
* not as a floating panel.
|
||||
*/
|
||||
export const shadow = {
|
||||
card: {
|
||||
shadowColor: "#0B0B12",
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 10,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
elevation: 1,
|
||||
},
|
||||
fab: {
|
||||
shadowColor: "#0B0B12",
|
||||
shadowOpacity: 0.22,
|
||||
shadowRadius: 14,
|
||||
shadowOffset: { width: 0, height: 6 },
|
||||
elevation: 6,
|
||||
},
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user