From 59c952aeb194b701595089df4910bceb004ab8e1 Mon Sep 17 00:00:00 2001 From: suiramdev Date: Fri, 13 Nov 2026 14:55:49 +0100 Subject: [PATCH] feat(web): animate Kubai icons from their parent button's hover and toggle state --- apps/web/AGENTS.md | 2 +- apps/web/src/app/shell/bottom-tab-bar.tsx | 5 +- .../src/app/shell/sidebar-first-step-icon.tsx | 10 +- apps/web/src/shared/lib/kubai-icon.tsx | 152 +++++++++++++++++- docs/engineering/web-ui.md | 1 + 5 files changed, 163 insertions(+), 7 deletions(-) diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md index ea09099c..11f88c57 100644 --- a/apps/web/AGENTS.md +++ b/apps/web/AGENTS.md @@ -179,7 +179,7 @@ scripts/generate-favicon.ts # public/favicon.svg + favicon.png Two icon libraries, each with one job. Pick by what the icon does, not by which one has a match. -- **Kubai Icons (`kubai-icons-react`) mark a destination or a primary action.** They are coloured, layered and animate on hover, so they belong where an icon names a place or a thing to do: the sidebar's sections (`src/app/shell/nav-items.ts`) and first steps (`src/app/shell/first-steps.ts`), the settings menu (`src/pages/settings/model/settings-nav-items.ts`), the phone tab bar (`src/app/shell/bottom-tab-bar.tsx`), the account menu's entries, and the theme and language switchers. The sync button keeps the Remix `RiRefreshLine`, like every other refresh control. Wrap one with `kubaiIcon(Icon, color)` from `@/shared/lib/kubai-icon` for an `IconComponent` slot. The colour is a `ChartColorVar`, one of the `CHART_COLOR_VARS` tokens in `@/shared/lib/chart-colors`, so it follows the theme and the type checker refuses a literal. A section keeps one icon and one colour everywhere it appears: Budget is a green `CurrencyDollar` in the settings menu and the first steps, and its Transactions page keeps that green on a `Receipt` in the sidebar and the tab bar. +- **Kubai Icons (`kubai-icons-react`) mark a destination or a primary action.** They are coloured, layered and animate on hover, so they belong where an icon names a place or a thing to do: the sidebar's sections (`src/app/shell/nav-items.ts`) and first steps (`src/app/shell/first-steps.ts`), the settings menu (`src/pages/settings/model/settings-nav-items.ts`), the phone tab bar (`src/app/shell/bottom-tab-bar.tsx`), the account menu's entries, and the theme and language switchers. The sync button keeps the Remix `RiRefreshLine`, like every other refresh control. Wrap one with `kubaiIcon(Icon, color)` from `@/shared/lib/kubai-icon` for an `IconComponent` slot, and render one directly as `HostAnimatedKubaiIcon`, never as the bare component: it animates when the surrounding button is hovered, focused or toggled, not only when the glyph itself is hovered. The colour is a `ChartColorVar`, one of the `CHART_COLOR_VARS` tokens in `@/shared/lib/chart-colors`, so it follows the theme and the type checker refuses a literal. A section keeps one icon and one colour everywhere it appears: Budget is a green `CurrencyDollar` in the settings menu and the first steps, and its Transactions page keeps that green on a `Receipt` in the sidebar and the tab bar. - **Remix Icon (`@remixicon/react`) draws every glyph that is an affordance rather than a meaning:** chevrons and disclosure arrows, a submenu's `›`, the up/down selector, a button's trailing arrow, the external-link mark, the sidebar toggle, close, check, search, spinners, and every icon inside a `@freenary/ui` primitive (they come from `src/shared/lib/ff-icons.ts` through the `IconProvider`). These stay monochrome in `currentColor`. Kubai draws arrows and chevrons on a filled disc, which reads as a button next to text, so never use Kubai for one. Wrap a Remix icon with `remixIcon(Icon)` from `@/shared/lib/remix-icon` for an `IconComponent` slot. - **Content icons stay on Remix too:** category icons (`entities/category`), row actions such as edit, delete, filter and bookmark, and status marks in notices. diff --git a/apps/web/src/app/shell/bottom-tab-bar.tsx b/apps/web/src/app/shell/bottom-tab-bar.tsx index 23f6b1be..58e3a45c 100644 --- a/apps/web/src/app/shell/bottom-tab-bar.tsx +++ b/apps/web/src/app/shell/bottom-tab-bar.tsx @@ -2,6 +2,7 @@ import { cn } from "@freenary/ui/lib/utils"; import { Link, useLocation } from "@tanstack/react-router"; import { m } from "@/paraglide/messages.js"; +import { HostAnimatedKubaiIcon } from "@/shared/lib/kubai-icon"; import { isCurrentNav, NAV_ITEMS } from "./nav-items"; @@ -20,7 +21,6 @@ export const BottomTabBar = () => {