diff --git a/apps/www/package.json b/apps/www/package.json index f66b19f81..8fcd3f29a 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -22,7 +22,7 @@ "fumadocs-mdx": "13.0.5", "fumadocs-typescript": "^4.0.6", "fumadocs-ui": "16.0.7", - "lucide-react": "^0.548.0", + "lucide-react": "^1.45.0", "next": "16.0.7", "next-themes": "^0.4.4", "prettier": "^2.8.8", diff --git a/apps/www/src/components/docs/github-mark.tsx b/apps/www/src/components/docs/github-mark.tsx new file mode 100644 index 000000000..319bb6502 --- /dev/null +++ b/apps/www/src/components/docs/github-mark.tsx @@ -0,0 +1,24 @@ +import type { SVGProps } from 'react'; + +/** + * The GitHub mark, drawn here rather than imported. + * + * lucide dropped its brand icons in 1.0, so `Github` no longer exists. A brand + * mark is the one kind of icon a general icon set can't carry — the shape + * belongs to its owner, not to the set — so it lives with the one link that + * uses it. + */ +export function GithubMark(props: SVGProps) { + return ( + + ); +} diff --git a/apps/www/src/components/docs/navbar.tsx b/apps/www/src/components/docs/navbar.tsx index babe95fc2..fe10bd3e3 100644 --- a/apps/www/src/components/docs/navbar.tsx +++ b/apps/www/src/components/docs/navbar.tsx @@ -2,12 +2,13 @@ import { Breadcrumb, Button, CopyIcon } from '@raystack/apsara'; import { useBreadcrumb } from 'fumadocs-core/breadcrumb'; import { Root } from 'fumadocs-core/page-tree'; -import { Component, Github } from 'lucide-react'; +import { Component } from 'lucide-react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { Fragment, useState } from 'react'; import { SourceType } from '@/lib/types'; import { useDemoContext } from '../demo/demo-context'; +import { GithubMark } from './github-mark'; import styles from './navbar.module.css'; const cache = new Map(); @@ -114,9 +115,7 @@ export default function DocsNavbar({ variant='outline' color='neutral' size='small' - leadingIcon={ - - } + leadingIcon={} > View source diff --git a/apps/www/src/content/docs/(overview)/upgrading.mdx b/apps/www/src/content/docs/(overview)/upgrading.mdx index f4a51c02d..51cbf7302 100644 --- a/apps/www/src/content/docs/(overview)/upgrading.mdx +++ b/apps/www/src/content/docs/(overview)/upgrading.mdx @@ -22,9 +22,9 @@ names `@raystack/apsara/icons` exports have changed. npm install lucide-react ``` -The range is wide, `>=0.500.0 <1.0.0`, so your app picks the version. If a -lucide release changes a drawing you care about, replace that one icon (step 5) -rather than pinning the whole library. +The range is wide, `>=0.500.0 <2.0.0`, so your app picks the version — both the +0.x and 1.x lines satisfy it. If a lucide release changes a drawing you care +about, replace that one icon (step 5) rather than pinning the whole library. `@radix-ui/react-icons` is no longer a dependency of Apsara. If your own code diff --git a/packages/raystack/CHANGELOG.md b/packages/raystack/CHANGELOG.md index 594a2dd4e..d7acbce8f 100644 --- a/packages/raystack/CHANGELOG.md +++ b/packages/raystack/CHANGELOG.md @@ -13,8 +13,10 @@ and [Icons](https://apsara.raystack.io/docs/theme/icons). #### Breaking changes -- **`lucide-react` is a new peer dependency**, range `>=0.500.0 <1.0.0`. - Install it. +- **`lucide-react` is a new peer dependency**, range `>=0.500.0 <2.0.0`, + so both the 0.x and 1.x lines satisfy it. Install it. Note that lucide + dropped its brand icons in 1.0, so a name like `Github` no longer + exists there — none of Apsara's 31 keys draw one. - **`@radix-ui/react-icons` is no longer a dependency.** If your own code imports from it, keep it in your own `dependencies`. - **`@raystack/apsara/icons` exports icon components, not raw SVG diff --git a/packages/raystack/icons/__tests__/lucide-contract.test.tsx b/packages/raystack/icons/__tests__/lucide-contract.test.tsx new file mode 100644 index 000000000..a72474d77 --- /dev/null +++ b/packages/raystack/icons/__tests__/lucide-contract.test.tsx @@ -0,0 +1,54 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { render } from '@testing-library/react'; +import * as lucide from 'lucide-react'; +import { describe, expect, it } from 'vitest'; +import { CheckIcon } from '../icons'; + +/** + * The peer range spans two lucide majors (`>=0.500.0 <2.0.0`), so what Apsara + * relies on has to hold across both. It relies on three things only, and this + * file pins each one: + * + * 1. every drawing it imports is still exported under that name, + * 2. `width` / `height` / `strokeWidth` are plain SVG props, and + * 3. the viewBox is 24 units, which is what makes `strokeWidth={1.5}` a 1px + * stroke at 16px. + * + * A lucide release that breaks any of them fails here rather than in a + * consumer's app. + */ + +/** vitest runs with the package root as the cwd. */ +const ICONS_SRC = resolve(process.cwd(), 'icons/icons.tsx'); + +/** The lucide names `icons.tsx` imports, read from its own import block. */ +function importedNames(): string[] { + const src = readFileSync(ICONS_SRC, 'utf8'); + const block = src.match(/import \{([\s\S]*?)\} from 'lucide-react';/); + if (!block) throw new Error('no lucide import block in icons.tsx'); + return block[1] + .split(',') + .map(name => name.trim()) + .filter(Boolean); +} + +describe('lucide contract', () => { + it('exports every drawing icons.tsx imports', () => { + const names = importedNames(); + + expect(names.length).toBeGreaterThan(0); + expect(names.filter(name => !(name in lucide))).toEqual([]); + }); + + it('takes size and stroke as SVG props, on a 24-unit viewBox', () => { + const { container } = render(); + const svg = container.querySelector('svg'); + + expect(svg).toHaveAttribute('width', '16'); + expect(svg).toHaveAttribute('height', '16'); + expect(svg).toHaveAttribute('stroke-width', '1.5'); + // Not 0 0 16 16: the rendered stroke is strokeWidth x size / 24. + expect(svg).toHaveAttribute('viewBox', '0 0 24 24'); + }); +}); diff --git a/packages/raystack/package.json b/packages/raystack/package.json index 1ea09e940..a8503d827 100644 --- a/packages/raystack/package.json +++ b/packages/raystack/package.json @@ -98,7 +98,7 @@ "dotenv": "^17.2.2", "identity-obj-proxy": "^3.0.0", "jsdom": "^26.1.0", - "lucide-react": "^0.548.0", + "lucide-react": "^1.45.0", "postcss": "^8.4.24", "postcss-import": "^16.1.0", "postcss-modules": "^6.0.0", @@ -135,7 +135,7 @@ }, "peerDependencies": { "@types/react": "^19", - "lucide-react": ">=0.500.0 <1.0.0", + "lucide-react": ">=0.500.0 <2.0.0", "react": "^19", "react-dom": "^19" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f7baba41..f7c2ddafd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,22 +55,22 @@ importers: version: 1.11.20 fumadocs-core: specifier: 16.0.7 - version: 16.0.7(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + version: 16.0.7(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) fumadocs-docgen: specifier: ^1.3.8 version: 1.3.8(typescript@5.9.3) fumadocs-mdx: specifier: 13.0.5 - version: 13.0.5(fumadocs-core@16.0.7(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(vite@7.1.3(@types/node@24.10.0)) + version: 13.0.5(fumadocs-core@16.0.7(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(vite@7.1.3(@types/node@24.10.0)) fumadocs-typescript: specifier: ^4.0.6 version: 4.0.6(@types/react@19.2.2)(typescript@5.9.3) fumadocs-ui: specifier: 16.0.7 - version: 16.0.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(tailwindcss@4.1.16) + version: 16.0.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(tailwindcss@4.1.16) lucide-react: - specifier: ^0.548.0 - version: 0.548.0(react@19.2.1) + specifier: ^1.45.0 + version: 1.45.0(react@19.2.1) next: specifier: 16.0.7 version: 16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1) @@ -270,8 +270,8 @@ importers: specifier: ^26.1.0 version: 26.1.0 lucide-react: - specifier: ^0.548.0 - version: 0.548.0(react@19.2.1) + specifier: ^1.45.0 + version: 1.45.0(react@19.2.1) postcss: specifier: ^8.4.24 version: 8.4.24 @@ -4190,8 +4190,8 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - lucide-react@0.548.0: - resolution: {integrity: sha512-63b16z63jM9yc1MwxajHeuu0FRZFsDtljtDjYm26Kd86UQ5HQzu9ksEtoUUw4RBuewodw/tGFmvipePvRsKeDA==} + lucide-react@1.45.0: + resolution: {integrity: sha512-yH1ubCAduho9UR7oJhRXIQXogksRILBiTuZC4/bQIGeB9JOkxMlSuEHyyZpo1Z3S0yWJO2KTSUZbjiNvVxeOUw==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -9458,7 +9458,7 @@ snapshots: fsevents@2.3.3: optional: true - fumadocs-core@16.0.7(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1): + fumadocs-core@16.0.7(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1): dependencies: '@formatjs/intl-localematcher': 0.6.2 '@orama/orama': 3.1.16 @@ -9480,7 +9480,7 @@ snapshots: unist-util-visit: 5.0.0 optionalDependencies: '@types/react': 19.2.2 - lucide-react: 0.548.0(react@19.2.1) + lucide-react: 1.45.0(react@19.2.1) next: 16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1) react: 19.2.1 react-dom: 19.2.1(react@19.2.1) @@ -9501,14 +9501,14 @@ snapshots: - supports-color - typescript - fumadocs-mdx@13.0.5(fumadocs-core@16.0.7(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(vite@7.1.3(@types/node@24.10.0)): + fumadocs-mdx@13.0.5(fumadocs-core@16.0.7(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(vite@7.1.3(@types/node@24.10.0)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.0.0 chokidar: 4.0.3 esbuild: 0.25.12 estree-util-value-to-estree: 3.5.0 - fumadocs-core: 16.0.7(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + fumadocs-core: 16.0.7(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) js-yaml: 4.1.0 lru-cache: 11.2.2 mdast-util-to-markdown: 2.1.2 @@ -9560,7 +9560,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-ui@16.0.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(tailwindcss@4.1.16): + fumadocs-ui@16.0.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(tailwindcss@4.1.16): dependencies: '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) @@ -9573,7 +9573,7 @@ snapshots: '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.1) '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) class-variance-authority: 0.7.1 - fumadocs-core: 16.0.7(@types/react@19.2.2)(lucide-react@0.548.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + fumadocs-core: 16.0.7(@types/react@19.2.2)(lucide-react@1.45.0(react@19.2.1))(next@16.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1) lodash.merge: 4.6.2 next-themes: 0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) postcss-selector-parser: 7.1.0 @@ -10382,7 +10382,7 @@ snapshots: dependencies: yallist: 4.0.0 - lucide-react@0.548.0(react@19.2.1): + lucide-react@1.45.0(react@19.2.1): dependencies: react: 19.2.1