@godxjp/ui 16.9.3 → 16.10.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.
@@ -1,7 +1,14 @@
1
- export type ProgressTone = "success" | "warning";
1
+ export type ProgressTone = "success" | "warning" | "destructive";
2
2
  export type ProgressProps = {
3
3
  value: number;
4
4
  label?: string;
5
5
  tone?: ProgressTone;
6
+ /**
7
+ * Allow `value` to exceed 100 and render an OVER-CAPACITY fill: the bar caps at 100% width but
8
+ * gets a diagonal striped overlay + destructive tone, so an over-limit meter (e.g. 252% of a
9
+ * booked weight) is unmistakably different from a full (100%) one. `aria-valuetext` reports the
10
+ * real ratio. Off by default — `value` clamps to 100 as before.
11
+ */
12
+ over?: boolean;
6
13
  };
7
- export declare function Progress({ value, label, tone }: ProgressProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function Progress({ value, label, tone, over }: ProgressProps): import("react/jsx-runtime").JSX.Element;
@@ -1,19 +1,22 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- function Progress({ value, label, tone = "success" }) {
4
+ function Progress({ value, label, tone, over = false }) {
5
+ const isOver = over && value > 100;
5
6
  const boundedValue = Math.max(0, Math.min(100, value));
7
+ const effectiveTone = tone ?? (isOver ? "destructive" : "success");
6
8
  const labelId = React.useId();
7
9
  return /* @__PURE__ */ jsxs(
8
10
  "div",
9
11
  {
10
12
  className: "ui-progress",
11
- "data-tone": tone,
13
+ "data-tone": effectiveTone,
14
+ "data-over": isOver ? "" : void 0,
12
15
  role: "progressbar",
13
16
  "aria-valuenow": boundedValue,
14
17
  "aria-valuemin": 0,
15
18
  "aria-valuemax": 100,
16
- "aria-valuetext": `${boundedValue}%`,
19
+ "aria-valuetext": `${isOver ? Math.round(value) : boundedValue}%`,
17
20
  "aria-labelledby": label ? labelId : void 0,
18
21
  "aria-label": label ? void 0 : "Progress",
19
22
  children: [
@@ -2,3 +2,5 @@ export { Button, buttonVariants } from "./button.js";
2
2
  export type { ButtonProps } from "./button.js";
3
3
  export { Text, Heading } from "./typography.js";
4
4
  export type { TextProps, HeadingProps, TextProp, HeadingProp } from "./typography.js";
5
+ export { Logo } from "./logo.js";
6
+ export type { LogoProps, LogoSize } from "./logo.js";
@@ -1,8 +1,10 @@
1
1
  import { Button, buttonVariants } from "./button.js";
2
2
  import { Text, Heading } from "./typography.js";
3
+ import { Logo } from "./logo.js";
3
4
  export {
4
5
  Button,
5
6
  Heading,
7
+ Logo,
6
8
  Text,
7
9
  buttonVariants
8
10
  };
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ export type LogoSize = "xs" | "sm" | "md" | "lg";
3
+ export interface LogoProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
4
+ /**
5
+ * The brand glyph — a short mark (a letter/initials, default `"g"`) or a custom node such as an
6
+ * inline `<svg>`. Keep it to 1–2 glyphs; the box is square and centres its content.
7
+ */
8
+ glyph?: React.ReactNode;
9
+ /** Box size tier (tokenised). Default `"md"` (1.75rem). */
10
+ size?: LogoSize;
11
+ /**
12
+ * Accessible name for the mark. When set, the logo is exposed to assistive tech as an image with
13
+ * this name; when omitted the mark is decorative (`aria-hidden`) — the correct default when a
14
+ * readable wordmark sits beside it.
15
+ */
16
+ label?: string;
17
+ }
18
+ /**
19
+ * Logo — the product brand-mark box: a glyph on the primary fill. Use it INSTEAD of hand-rolling a
20
+ * bare span with a fixed square size, a literal radius, and `bg-primary` + type utilities in shell
21
+ * headers, auth screens, and topbars — that repeats literal size/radius and puts type utilities on
22
+ * a bare span (rules #45/#46). Size, radius, and per-tier font-size are tokens; the fill reads the
23
+ * primary role, so a re-themed `--primary` re-tints the mark automatically. Pair with a wordmark for
24
+ * the full lockup (leave `label` unset so the mark stays decorative and the wordmark carries the
25
+ * accessible name).
26
+ */
27
+ export declare const Logo: React.ForwardRefExoticComponent<LogoProps & React.RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,23 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils.js";
4
+ const Logo = React.forwardRef(
5
+ ({ glyph = "g", size = "md", label, className, ...props }, ref) => /* @__PURE__ */ jsx(
6
+ "span",
7
+ {
8
+ ref,
9
+ "data-slot": "logo",
10
+ "data-size": size,
11
+ className: cn("ui-logo", className),
12
+ role: label ? "img" : void 0,
13
+ "aria-label": label,
14
+ "aria-hidden": label ? void 0 : true,
15
+ ...props,
16
+ children: glyph
17
+ }
18
+ )
19
+ );
20
+ Logo.displayName = "Logo";
21
+ export {
22
+ Logo
23
+ };
@@ -56,6 +56,24 @@
56
56
  background: hsl(var(--warning));
57
57
  }
58
58
 
59
+ .ui-progress[data-tone="destructive"] .ui-progress-bar {
60
+ background: hsl(var(--destructive));
61
+ }
62
+
63
+ /* Over-capacity: keep the destructive fill but lay a diagonal hatch over it so an over-limit bar
64
+ * (value > 100, width still capped at 100%) is visually distinct from a full one. Placed after the
65
+ * tone rules so background-color wins; background-image carries the stripes. */
66
+ .ui-progress[data-over] .ui-progress-bar {
67
+ background-color: hsl(var(--destructive));
68
+ background-image: repeating-linear-gradient(
69
+ 45deg,
70
+ hsl(var(--destructive-foreground) / 0.4) 0,
71
+ hsl(var(--destructive-foreground) / 0.4) 0.3rem,
72
+ transparent 0.3rem,
73
+ transparent 0.6rem
74
+ );
75
+ }
76
+
59
77
  .ui-progress-label {
60
78
  color: hsl(var(--muted-foreground));
61
79
  font-size: var(--progress-label-font-size);
@@ -17,6 +17,7 @@
17
17
  @import "./shell-layout.css";
18
18
  @import "./layout.css";
19
19
  @import "./control.css";
20
+ @import "./logo-layout.css";
20
21
  @import "./card-layout.css";
21
22
  @import "./text-layout.css";
22
23
  @import "./table-layout.css";
@@ -0,0 +1,41 @@
1
+ /*
2
+ * LOGO — the product brand-mark box (a glyph on the primary fill). Replaces the hand-rolled
3
+ * `<span aria-hidden className="grid size-7 place-items-center rounded-md bg-primary …">g</span>`
4
+ * repeated across app shells and topbars (issue #116): size/radius/font-size are tokens (rule #45),
5
+ * colours read the primary role directly, and the type comes from a semantic class (not utilities
6
+ * on a bare span, rule #46).
7
+ */
8
+ @layer components {
9
+ .ui-logo {
10
+ display: inline-grid;
11
+ place-items: center;
12
+ flex: 0 0 auto;
13
+ width: var(--logo-size-md);
14
+ height: var(--logo-size-md);
15
+ border-radius: var(--logo-radius);
16
+ background: hsl(var(--primary));
17
+ color: hsl(var(--primary-foreground));
18
+ font-weight: 700;
19
+ font-size: var(--logo-font-size-md);
20
+ line-height: 1;
21
+ user-select: none;
22
+ }
23
+
24
+ .ui-logo[data-size="xs"] {
25
+ width: var(--logo-size-xs);
26
+ height: var(--logo-size-xs);
27
+ font-size: var(--logo-font-size-xs);
28
+ }
29
+
30
+ .ui-logo[data-size="sm"] {
31
+ width: var(--logo-size-sm);
32
+ height: var(--logo-size-sm);
33
+ font-size: var(--logo-font-size-sm);
34
+ }
35
+
36
+ .ui-logo[data-size="lg"] {
37
+ width: var(--logo-size-lg);
38
+ height: var(--logo-size-lg);
39
+ font-size: var(--logo-font-size-lg);
40
+ }
41
+ }
@@ -0,0 +1,43 @@
1
+ /* ───────────────────────────────────────────────────────────────
2
+ * Famgia service theme — ファムジア株式会社
3
+ * Re-themes @godxjp/ui via SEMANTIC TOKENS only (no component CSS).
4
+ * Brand: electric blue #2563EB (primary) + violet #7C3AED (accent gradient).
5
+ * Import this instead of "@godxjp/ui/styles".
6
+ * ─────────────────────────────────────────────────────────────── */
7
+
8
+ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+JP:wght@400;500;700&display=swap");
9
+ @import "@godxjp/ui/styles";
10
+
11
+ /* ── Light (default) ── */
12
+ :root {
13
+ /* Brand primary ramp — electric blue */
14
+ --primary: 221 83% 53%; /* #2563EB */
15
+ --primary-foreground: 0 0% 100%;
16
+ --primary-hover: 224 71% 48%; /* #1D4ED8 */
17
+ --primary-active: 226 71% 40%; /* #1E40AF */
18
+ --ring: 221 83% 53%;
19
+
20
+ /* Brand-colored text (AA on tint/white) */
21
+ --text-link: 224 76% 45%;
22
+ --text-brand: 221 83% 53%;
23
+ --text-primary: 224 76% 42%;
24
+
25
+ /* Signature brand gradient: blue → violet */
26
+ --gradient-brand: linear-gradient(135deg, hsl(221 83% 53%), hsl(262 83% 58%));
27
+
28
+ /* Typography — Inter (Latin/VI) + Noto Sans JP (kana/kanji) */
29
+ --font-family-sans: "Inter", "Noto Sans JP", -apple-system, BlinkMacSystemFont,
30
+ "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
31
+ }
32
+
33
+ /* ── Dark ── lifted blue for contrast */
34
+ .dark {
35
+ --primary: 221 90% 66%;
36
+ --primary-foreground: 222 47% 11%;
37
+ --primary-hover: 221 90% 72%;
38
+ --primary-active: 221 90% 60%;
39
+ --ring: 221 90% 66%;
40
+ --text-link: 221 90% 72%;
41
+ --text-brand: 221 90% 70%;
42
+ --text-primary: 221 90% 68%;
43
+ }
@@ -16,5 +16,6 @@
16
16
  @import "./components/navigation.css";
17
17
  @import "./components/data-display.css";
18
18
  @import "./components/list-row.css";
19
+ @import "./components/logo.css";
19
20
  @import "./components/data-entry.css";
20
21
  @import "./components/shell.css";
@@ -0,0 +1,15 @@
1
+ /* Logo component tokens — the product brand-mark box (a glyph on the primary fill), used in the
2
+ * app shell header, auth screens, and topbars. Size + radius + per-tier font-size are knobs so a
3
+ * service theme retunes the mark without forking CSS (rules #44/#45). Colours read the primary
4
+ * role tokens directly, so a re-themed --primary re-tints the mark automatically. */
5
+ :root {
6
+ --logo-radius: var(--radius);
7
+ --logo-size-xs: 1.25rem;
8
+ --logo-size-sm: 1.5rem;
9
+ --logo-size-md: 1.75rem;
10
+ --logo-size-lg: 2.25rem;
11
+ --logo-font-size-xs: var(--font-size-2xs);
12
+ --logo-font-size-sm: var(--font-size-xs);
13
+ --logo-font-size-md: var(--font-size-sm);
14
+ --logo-font-size-lg: var(--font-size-base);
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "16.9.3",
3
+ "version": "16.10.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@10.29.1",
6
6
  "sideEffects": false,
@@ -259,7 +259,12 @@ const RULES = [
259
259
  {
260
260
  id: "raw-intl-date",
261
261
  severity: "warn",
262
- test: /\.toLocale(?:Date|Time)?String\(\s*\)|new Date\([^)]*\)\.(?:getMonth|getDate|getFullYear)\(\)\s*\+/,
262
+ // Only the unambiguously-Date methods: `.toLocaleDateString()` / `.toLocaleTimeString()`.
263
+ // Bare `.toLocaleString()` is excluded on purpose — it is overwhelmingly
264
+ // `Number.prototype.toLocaleString()` (legit locale number formatting), and matching it
265
+ // false-flagged every formatted count/amount. A date+time `Date.toLocaleString()` is rare and
266
+ // still partly covered by the hand-built `new Date(...).getMonth()+` alternative below.
267
+ test: /\.toLocale(?:Date|Time)String\(\s*\)|new Date\([^)]*\)\.(?:getMonth|getDate|getFullYear)\(\)\s*\+/,
263
268
  standard: "ISO 8601 · IANA tz database · ECMA-402 Intl.DateTimeFormat",
264
269
  message:
265
270
  "Don't hand-build or locale-default dates. Use formatDate from @godxjp/ui/datetime (Intl.DateTimeFormat + IANA timezone + ISO-8601), which respects the AppProvider locale/timezone.",