@vvedantb/ui 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -879,7 +879,7 @@ links under a separator.
879
879
  ### `Icon*` app icons
880
880
 
881
881
  Tabler-style plain app glyphs: `IconVibot`, `IconVhub`, `IconVmem`, `IconVerve`,
882
- `IconVelth` and `IconProjectV`. No background, no padding. `APP_ICONS[app]`
882
+ `IconVelth` and `IconProjectV`. No background. Each sits on an equalised square viewBox, so all apps look the same size. `APP_ICONS[app]`
883
883
  looks one up by `AppName`.
884
884
 
885
885
  ```tsx
@@ -888,7 +888,7 @@ import { IconVerve } from "@vvedantb/ui"
888
888
  <IconVerve size={24} color="currentColor" />
889
889
  ```
890
890
 
891
- `size` is the height (default 24) and the width follows the glyph. `color`
891
+ `size` sets both width and height (default 24; a string is any CSS length). `color`
892
892
  defaults to `currentColor`. `stroke` sets the stroke width in pixels for stroke
893
893
  glyphs (fill glyphs ignore it). `title` gives an accessible name. The ref goes
894
894
  to the `<svg>`.
@@ -899,13 +899,16 @@ Deprecated in favour of the `Icon*` components for plain marks. Tiles stay here.
899
899
  Family app icons: `app` (`projectv`, `vibot`, `vhub`, `vmem`, `verve`, `velth`),
900
900
  `variant`, `size`, `title`.
901
901
 
902
- - `variant="mark"` (default) is the plain logo in `currentColor`. It is cropped
903
- tight to the glyph (stroke marks include half the stroke width) with no
904
- padding. `size` is the rendered height; the width follows the glyph's aspect
905
- ratio, so it sits level with a wordmark. A string `size` (for example `"2rem"`)
906
- sets both width and height to that value. `APP_MARK_VIEWBOX` holds the tight
907
- viewBox per app; `appMarkSvg()` and `APP_MARK_SVG` use it.
908
- - `variant="tile"` and `"tile-light"` keep the equalised, padded 1024 grid on a
902
+ - `variant="mark"` (default) is the plain logo in `currentColor`. It sits
903
+ on a square viewBox. The five apps (`vibot`, `vhub`, `vmem`, `verve`, `velth`) share one
904
+ crop (`APP_MARK_CROP_VIEWBOX`, `173 197 629 629`): the union of their glyph boxes on the
905
+ 1024 tile grid, centred, with about 4% padding, so relative glyph sizes match the tiles and
906
+ the widest glyph fills about 92% of the icon box. `projectv` is a brand mark, so it has its
907
+ own tight square (`PROJECTV_MARK_VIEWBOX`, `172 181 687 687`). `size` sets both width and
908
+ height; a string `size` (for example `"2rem"`) is any CSS length. `APP_MARK_VIEWBOX` holds the
909
+ viewBox for each app; `appMarkSvg()` and `APP_MARK_SVG` use it. `markAspect` returns
910
+ 1 and is kept for API stability only.
911
+ - `variant="tile"` and `"tile-light"` use the same 1024 grid on a
909
912
  rounded tile, always square. `appTileSvg`, `appAdaptiveSvg`, `appExtension128Svg`,
910
913
  `APP_TILE_SVG`, `APP_TILE_LIGHT_SVG` and favicons are unchanged.
911
914
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vvedantb/ui",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "license": "MIT",
5
5
  "description": "Shared React 19 UI for web apps: one JavaScript entry (primitives, shell pieces, app helpers including the Convex and Clerk provider, VITE env parsing and ensure-user, artifact viewers over the @vvedantb/server artifacts core, native tokens), design-token CSS, shared Tailwind v4 CSS and a Tailwind preset.",
6
6
  "type": "module",
@@ -1,8 +1,8 @@
1
1
  import { forwardRef, useId, type ForwardRefExoticComponent, type RefAttributes, type SVGProps } from "react";
2
- import { APP_MARKS, APP_MARK_VIEWBOX, APP_NAMES, markAspect, markLinecap, markStrokeWidth, type AppName } from "./svg";
2
+ import { APP_MARKS, APP_MARK_VIEWBOX, APP_NAMES, markLinecap, markStrokeWidth, type AppName } from "./svg";
3
3
 
4
4
  export type AppIconProps = Omit<SVGProps<SVGSVGElement>, "children" | "height" | "width" | "viewBox" | "stroke" | "color"> & {
5
- /** Rendered height. Default 24. A number gives the width from the glyph's aspect ratio; a string sets width and height to the same value. */
5
+ /** Rendered width and height (the glyph is square). Default 24. A number is in pixels; a string is any CSS length. */
6
6
  size?: number | string;
7
7
  /** Glyph colour, for fill and stroke glyphs. Default `"currentColor"`. */
8
8
  color?: string;
@@ -14,7 +14,7 @@ export type AppIconProps = Omit<SVGProps<SVGSVGElement>, "children" | "height" |
14
14
 
15
15
  export type AppIconComponent = ForwardRefExoticComponent<AppIconProps & RefAttributes<SVGSVGElement>>;
16
16
 
17
- function viewBoxHeight(app: AppName): number {
17
+ function viewBoxSide(app: AppName): number {
18
18
  return Number(APP_MARK_VIEWBOX[app].split(" ")[3]);
19
19
  }
20
20
 
@@ -25,9 +25,8 @@ function createAppIcon(app: AppName, displayName: string): AppIconComponent {
25
25
  ) {
26
26
  const titleId = useId();
27
27
  const { kind, paths } = APP_MARKS[app];
28
- const width = typeof size === "number" ? Math.round(size * markAspect(app) * 100) / 100 : size;
29
- const renderedHeight = typeof size === "number" ? size : 24;
30
- const strokeWidth = stroke === undefined ? markStrokeWidth(app) : Number(stroke) * (viewBoxHeight(app) / renderedHeight);
28
+ const renderedSize = typeof size === "number" ? size : 24;
29
+ const strokeWidth = stroke === undefined ? markStrokeWidth(app) : Number(stroke) * (viewBoxSide(app) / renderedSize);
31
30
  const glyphProps =
32
31
  kind === "fill"
33
32
  ? { fill: color }
@@ -37,7 +36,7 @@ function createAppIcon(app: AppName, displayName: string): AppIconComponent {
37
36
  {...rest}
38
37
  ref={ref}
39
38
  xmlns="http://www.w3.org/2000/svg"
40
- width={width}
39
+ width={size}
41
40
  height={size}
42
41
  viewBox={APP_MARK_VIEWBOX[app]}
43
42
  preserveAspectRatio="xMidYMid meet"
@@ -5,12 +5,10 @@ import { APP_MARKS, MARK_VIEWBOX, TILE_RADIUS, markLinecap, markStrokeWidth, typ
5
5
  export type AppMarkProps = Omit<SVGProps<SVGSVGElement>, "children" | "height" | "width" | "viewBox"> & {
6
6
  /** Which family app's mark to draw. */
7
7
  app: AppName;
8
- /** `"mark"` (default): plain logo in `currentColor`, cropped tight to the glyph with no padding. `"tile"`: white glyph on a black rounded tile. `"tile-light"`: black glyph on a white tile. */
8
+ /** `"mark"` (default): plain logo in `currentColor` on an equalised square viewBox, so every app looks the same size. `"tile"`: white glyph on a black rounded tile. `"tile-light"`: black glyph on a white tile. */
9
9
  variant?: AppMarkVariant;
10
10
  /**
11
- * Default 24. For `"mark"` it sets the rendered height and the width follows the glyph's aspect ratio
12
- * (a number gives an exact width; a string sets both to the same value and the glyph is centred).
13
- * Tiles are always square, `size` by `size`.
11
+ * Default 24. Sets both width and height, for marks and tiles alike (always square). A string is any CSS length.
14
12
  */
15
13
  size?: number | string;
16
14
  /** Accessible name. Without it the mark is `aria-hidden`. */
@@ -23,7 +21,7 @@ const TILE_TONES = {
23
21
  } as const;
24
22
 
25
23
  /**
26
- * One of the six family app marks. The plain mark is cropped tight; tiles use the padded 1024 grid.
24
+ * One of the six family app marks. The plain mark and the tiles share the same square 1024 viewBox, so a bare glyph matches the tile glyph optically.
27
25
  * @deprecated Use the per-app `Icon*` components (or `APP_ICONS[app]`) for plain marks. Tiles are for app and store icons only.
28
26
  */
29
27
  export function AppMark({ app, variant = "mark", size = 24, title, ...rest }: AppMarkProps) {
@@ -3,7 +3,9 @@ export { AppMark } from "./AppMark";
3
3
  export type { AppMarkProps } from "./AppMark";
4
4
  export {
5
5
  APP_MARKS,
6
+ APP_MARK_CROP_VIEWBOX,
6
7
  APP_MARK_VIEWBOX,
8
+ PROJECTV_MARK_VIEWBOX,
7
9
  APP_MARK_SVG,
8
10
  APP_NAMES,
9
11
  APP_TILE_LIGHT_SVG,
package/src/brand/svg.ts CHANGED
@@ -5,7 +5,7 @@ export const APP_NAMES = ["projectv", "vibot", "vhub", "vmem", "verve", "velth"]
5
5
 
6
6
  export type AppName = (typeof APP_NAMES)[number];
7
7
 
8
- /** `mark`: glyph only, cropped tight, follows `currentColor`. `tile`: white glyph on a black tile. `tile-light`: black glyph on a white tile. */
8
+ /** `mark`: glyph only on an equalised square viewBox, follows `currentColor`. `tile`: white glyph on a black tile. `tile-light`: black glyph on a white tile. */
9
9
  export type AppMarkVariant = "mark" | "tile" | "tile-light";
10
10
 
11
11
  export const MARK_VIEWBOX = "0 0 1024 1024";
@@ -175,20 +175,30 @@ export function markBBox(app: AppName): Box {
175
175
 
176
176
 
177
177
  /**
178
- * Tight viewBox per app (`minX minY width height` on the 1024 grid): the glyph box grown by half the
179
- * stroke width for stroke marks, rounded outward to whole units. Checked against `markBBox` in tests.
180
- * Used by the plain logo (`variant="mark"`, `appMarkSvg`). Tiles keep `MARK_VIEWBOX`.
178
+ * Shared crop for the five app glyphs (`vibot`, `vhub`, `vmem`, `verve`, `velth`) in the plain logo (`variant="mark"`,
179
+ * `appMarkSvg`, `Icon*`): the union of their `markBBox` results on the 1024 tile grid (x 186 to 789, y 259.96 to 761.7),
180
+ * centred in a square of side max(width, height) x 1.04 (about 4% padding), then rounded outward to whole units.
181
+ * `projectv` is left out so its wide brand mark does not shrink the app glyphs. Not the full tile. Checked in tests.
181
182
  */
183
+ export const APP_MARK_CROP_VIEWBOX = "173 197 629 629";
184
+
185
+ /**
186
+ * Own tight square for `projectv` (a brand mark, not an app): `markBBox("projectv")` (x 172 to 858, y 377 to 672),
187
+ * centred on the shorter axis and rounded outward to whole units, with no extra padding.
188
+ */
189
+ export const PROJECTV_MARK_VIEWBOX = "172 181 687 687";
190
+
191
+ /** Plain-mark viewBox per app: the shared crop for the five apps, a tight square for `projectv`. Always square. */
182
192
  export const APP_MARK_VIEWBOX: Record<AppName, string> = {
183
- projectv: "172 377 686 295",
184
- vibot: "295 314 494 398",
185
- vhub: "186 338 582 348",
186
- vmem: "255 259 513 503",
187
- verve: "260 319 504 413",
188
- velth: "273 276 478 482",
193
+ projectv: PROJECTV_MARK_VIEWBOX,
194
+ vibot: APP_MARK_CROP_VIEWBOX,
195
+ vhub: APP_MARK_CROP_VIEWBOX,
196
+ vmem: APP_MARK_CROP_VIEWBOX,
197
+ verve: APP_MARK_CROP_VIEWBOX,
198
+ velth: APP_MARK_CROP_VIEWBOX,
189
199
  };
190
200
 
191
- /** Width over height of a mark's tight viewBox. */
201
+ /** Width over height of a mark's viewBox. Always about 1, because the viewBoxes are square. Kept for API stability; do not use it for sizing. */
192
202
  export function markAspect(app: AppName): number {
193
203
  const [, , w = 1, h = 1] = APP_MARK_VIEWBOX[app].split(" ").map(Number);
194
204
  return w / h;
@@ -205,7 +215,7 @@ function pathElements(app: AppName): string {
205
215
  return APP_MARKS[app].paths.map((d) => `<path d="${d}"/>`).join("");
206
216
  }
207
217
 
208
- /** Glyph only, cropped tight with no padding, painted with `color` (default `currentColor`). */
218
+ /** Glyph only on its equalised square viewBox, painted with `color` (default `currentColor`). */
209
219
  export function appMarkSvg(app: AppName, color = "currentColor"): string {
210
220
  return `<svg ${NS} viewBox="${APP_MARK_VIEWBOX[app]}" ${markPaintAttrs(app, color)}>${pathElements(app)}</svg>\n`;
211
221
  }