@bearmenu/ui 0.8.54 → 0.8.55

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.
@@ -78,6 +78,7 @@ var MenuItem = memo(function MenuItem2({
78
78
  onSignatureEvidence,
79
79
  onMentions,
80
80
  countKind = "mentions",
81
+ emphasis = "price",
81
82
  externalRating,
82
83
  formatRating = (score) => score.toFixed(1),
83
84
  markGradient,
@@ -206,7 +207,16 @@ var MenuItem = memo(function MenuItem2({
206
207
  (minPrice != null || hasQuantity || mentionCount > 0 || ratingText != null) && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex items-baseline justify-between gap-x-2 gap-y-1", children: [
207
208
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-baseline gap-x-2", children: [
208
209
  minPrice != null && /* @__PURE__ */ jsxs("span", { className: "flex items-baseline gap-1", children: [
209
- /* @__PURE__ */ jsx("span", { className: "text-[15px] font-bold tabular-nums text-foreground", children: isPriceRange ? `${formatPrice(minPrice)}\u2013${formatPrice(maxPrice)}` : formatPrice(minPrice) }),
210
+ /* @__PURE__ */ jsx(
211
+ "span",
212
+ {
213
+ className: cn(
214
+ "tabular-nums",
215
+ emphasis === "praise" ? "text-[13px] font-medium text-muted-foreground" : "text-[15px] font-bold text-foreground"
216
+ ),
217
+ children: isPriceRange ? `${formatPrice(minPrice)}\u2013${formatPrice(maxPrice)}` : formatPrice(minPrice)
218
+ }
219
+ ),
210
220
  /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: labels.currency })
211
221
  ] }),
212
222
  hasQuantity && /* @__PURE__ */ jsxs("span", { className: "text-[12px] tabular-nums text-muted-foreground", children: [
@@ -3,10 +3,10 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const alertVariants: (props?: ({
6
- variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
6
+ variant?: "default" | "destructive" | "warning" | "success" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
9
- variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
9
+ variant?: "default" | "destructive" | "warning" | "success" | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
11
11
  declare const AlertTitle: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLParagraphElement>>;
12
12
  declare const AlertDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "secondary" | "success" | "warning" | "default" | "destructive" | "outline" | "glass" | "overlay" | "brown" | "code" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
7
+ variant?: "code" | "default" | "destructive" | "outline" | "secondary" | "glass" | "overlay" | "brown" | "warning" | "success" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
8
8
  size?: "status" | "chip" | null | undefined;
9
9
  live?: boolean | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -31,7 +31,7 @@ import { VariantProps } from 'class-variance-authority';
31
31
  * right edge, centred on the text, and never wraps under it.
32
32
  */
33
33
  declare const bannerVariants: (props?: ({
34
- tone?: "warning" | "destructive" | "info" | null | undefined;
34
+ tone?: "destructive" | "warning" | "info" | null | undefined;
35
35
  } & class_variance_authority_types.ClassProp) | undefined) => string;
36
36
  type BannerProps = Omit<React.HTMLAttributes<HTMLDivElement>, "title"> & VariantProps<typeof bannerVariants> & {
37
37
  /**
@@ -3,8 +3,8 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "secondary" | "link" | "warning" | "default" | "destructive" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
7
- size?: "icon" | "default" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
+ variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "warning" | "cta" | null | undefined;
7
+ size?: "default" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
8
8
  fullWidth?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -3,12 +3,12 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const cardVariants: (props?: ({
6
- variant?: "outline" | "glass" | "section" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
6
+ variant?: "section" | "outline" | "glass" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  interactive?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
11
- variant?: "outline" | "glass" | "section" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
11
+ variant?: "section" | "outline" | "glass" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
12
12
  size?: "default" | "sm" | "lg" | null | undefined;
13
13
  interactive?: boolean | null | undefined;
14
14
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
@@ -4,11 +4,11 @@ import { DialogProps } from '@radix-ui/react-dialog';
4
4
 
5
5
  declare const Command: React.ForwardRefExoticComponent<Omit<{
6
6
  children?: React.ReactNode;
7
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
7
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
8
8
  ref?: React.Ref<HTMLDivElement>;
9
9
  } & {
10
10
  asChild?: boolean;
11
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
11
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
12
12
  label?: string;
13
13
  shouldFilter?: boolean;
14
14
  filter?: (value: string, search: string, keywords?: string[]) => number;
@@ -28,61 +28,61 @@ declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<
28
28
  ref?: React.Ref<HTMLInputElement>;
29
29
  } & {
30
30
  asChild?: boolean;
31
- }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "value" | "type"> & {
31
+ }, "key" | "asChild" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "type" | "value"> & {
32
32
  value?: string;
33
33
  onValueChange?: (search: string) => void;
34
34
  } & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
35
35
  declare const CommandList: React.ForwardRefExoticComponent<Omit<{
36
36
  children?: React.ReactNode;
37
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
37
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
38
38
  ref?: React.Ref<HTMLDivElement>;
39
39
  } & {
40
40
  asChild?: boolean;
41
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
41
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
42
42
  label?: string;
43
43
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
44
44
  declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
45
45
  children?: React.ReactNode;
46
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
46
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
47
47
  ref?: React.Ref<HTMLDivElement>;
48
48
  } & {
49
49
  asChild?: boolean;
50
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
50
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
51
51
  declare const CommandLoading: React.ForwardRefExoticComponent<Omit<{
52
52
  children?: React.ReactNode;
53
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
53
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
54
54
  ref?: React.Ref<HTMLDivElement>;
55
55
  } & {
56
56
  asChild?: boolean;
57
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
57
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
58
58
  progress?: number;
59
59
  label?: string;
60
60
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
61
61
  declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
62
62
  children?: React.ReactNode;
63
- } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
63
+ } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
64
64
  ref?: React.Ref<HTMLDivElement>;
65
65
  } & {
66
66
  asChild?: boolean;
67
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "heading" | "value"> & {
67
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "heading" | "value"> & {
68
68
  heading?: React.ReactNode;
69
69
  value?: string;
70
70
  forceMount?: boolean;
71
71
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
72
- declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
72
+ declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
73
73
  ref?: React.Ref<HTMLDivElement>;
74
74
  } & {
75
75
  asChild?: boolean;
76
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
76
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
77
77
  alwaysRender?: boolean;
78
78
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
79
79
  declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
80
80
  children?: React.ReactNode;
81
- } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
81
+ } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
82
82
  ref?: React.Ref<HTMLDivElement>;
83
83
  } & {
84
84
  asChild?: boolean;
85
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "onSelect" | "value" | "disabled"> & {
85
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "onSelect" | "disabled" | "value"> & {
86
86
  disabled?: boolean;
87
87
  onSelect?: (value: string) => void;
88
88
  value?: string;
@@ -53,7 +53,7 @@ interface EmptyStateProps {
53
53
  * louder than a load failure the user can retry.
54
54
  */
55
55
  declare const EmptyState: React.ForwardRefExoticComponent<EmptyStateProps & React.RefAttributes<HTMLDivElement>>;
56
- declare const SearchEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "icon" | "variant"> & React.RefAttributes<HTMLDivElement>>;
57
- declare const ErrorEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "icon" | "variant"> & React.RefAttributes<HTMLDivElement>>;
56
+ declare const SearchEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "variant" | "icon"> & React.RefAttributes<HTMLDivElement>>;
57
+ declare const ErrorEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "variant" | "icon"> & React.RefAttributes<HTMLDivElement>>;
58
58
 
59
59
  export { EmptyState, ErrorEmptyState, SearchEmptyState };
@@ -103,6 +103,14 @@ type MenuItemProps = {
103
103
  * Defaults to `"mentions"`. Keep `labels.mentions` in step with it.
104
104
  */
105
105
  countKind?: MenuItemCountKind;
106
+ /**
107
+ * What the meta row leads with. `"price"` (default): the price is bold, the
108
+ * count's peer at the other end. `"praise"`: on surfaces where reviews'
109
+ * praise is the point (the map's lists), the price steps down to secondary
110
+ * text, a fact to check rather than the row's headline, and the praise count
111
+ * keeps the bold accent.
112
+ */
113
+ emphasis?: "price" | "praise";
106
114
  /**
107
115
  * An outside aggregate score for the item (a wine's Vivino rating, a beer's
108
116
  * Untappd one), shown as a star and the score beside the mention count.
@@ -1,4 +1,4 @@
1
- export { ExternalRatingSignal, MentionSignal, MenuItem } from '../chunk-6Z6MBILC.js';
1
+ export { ExternalRatingSignal, MentionSignal, MenuItem } from '../chunk-CBBECMBD.js';
2
2
  import '../chunk-6PQINSAA.js';
3
3
  import '../chunk-RYPPN2GZ.js';
4
4
  import '../chunk-FEK5XGZW.js';
@@ -4,11 +4,11 @@ import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const spinnerVariants: (props?: ({
6
6
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
7
- tone?: "primary" | "inherit" | "default" | "destructive" | null | undefined;
7
+ tone?: "inherit" | "default" | "destructive" | "primary" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare const Spinner: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
10
10
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
11
- tone?: "primary" | "inherit" | "default" | "destructive" | null | undefined;
11
+ tone?: "inherit" | "default" | "destructive" | "primary" | null | undefined;
12
12
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
13
13
  /** Visually-hidden label for screen readers. Defaults to "Loading". */
14
14
  label?: string;
@@ -93,7 +93,9 @@ var TabsTrigger = React.forwardRef((_a, ref) => {
93
93
  transition: archetype.indicatorMorph,
94
94
  className: cn(
95
95
  "absolute inset-0 -z-0 motion-reduce:hidden",
96
- variant === "pill" && "rounded-xl bg-background shadow-sm",
96
+ // Dark: `bg-background` is darker than the `bg-muted` track, so the
97
+ // selection read as a hole; it lifts to the raised surface instead.
98
+ variant === "pill" && "rounded-xl bg-background shadow-sm dark:bg-surface-2 dark:ring-1 dark:ring-border",
97
99
  variant === "underline" && orientation === "horizontal" && "border-b-2 border-primary",
98
100
  variant === "underline" && orientation === "vertical" && "border-r-2 border-primary"
99
101
  )
@@ -105,7 +107,9 @@ var TabsTrigger = React.forwardRef((_a, ref) => {
105
107
  "aria-hidden": true,
106
108
  className: cn(
107
109
  "absolute inset-0 -z-0 hidden motion-reduce:block",
108
- variant === "pill" && "rounded-xl bg-background shadow-sm",
110
+ // Dark: `bg-background` is darker than the `bg-muted` track, so the
111
+ // selection read as a hole; it lifts to the raised surface instead.
112
+ variant === "pill" && "rounded-xl bg-background shadow-sm dark:bg-surface-2 dark:ring-1 dark:ring-border",
109
113
  variant === "underline" && orientation === "horizontal" && "border-b-2 border-primary",
110
114
  variant === "underline" && orientation === "vertical" && "border-r-2 border-primary"
111
115
  )
@@ -3,11 +3,11 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const tagVariants: (props?: ({
6
- variant?: "primary" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
6
+ variant?: "default" | "destructive" | "outline" | "warning" | "primary" | "success" | "quiet" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare const Tag: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
10
- variant?: "primary" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
10
+ variant?: "default" | "destructive" | "outline" | "warning" | "primary" | "success" | "quiet" | null | undefined;
11
11
  size?: "default" | "sm" | "lg" | null | undefined;
12
12
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
13
13
  onRemove?: () => void;
@@ -22,8 +22,8 @@ import { VariantProps } from 'class-variance-authority';
22
22
  * Weights are 400 / 600 / 700. 500 is not in the system.
23
23
  */
24
24
  declare const textVariants: (props?: ({
25
- variant?: "small" | "body" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
26
- color?: "primary" | "inherit" | "default" | "destructive" | "muted" | "primary-foreground" | null | undefined;
25
+ variant?: "h2" | "h3" | "label" | "body" | "caption" | "h1" | "h4" | "small" | "display" | "body-lg" | "body-sm" | "overline" | null | undefined;
26
+ color?: "inherit" | "default" | "destructive" | "primary" | "muted" | "primary-foreground" | null | undefined;
27
27
  weight?: "bold" | "medium" | "normal" | "semibold" | null | undefined;
28
28
  leading?: "none" | "normal" | "tight" | "snug" | "relaxed" | "loose" | null | undefined;
29
29
  tracking?: "normal" | "tight" | "wide" | "wider" | null | undefined;
@@ -1,4 +1,4 @@
1
- import { MenuItem } from '../chunk-6Z6MBILC.js';
1
+ import { MenuItem } from '../chunk-CBBECMBD.js';
2
2
  import '../chunk-6PQINSAA.js';
3
3
  import { Separator } from '../chunk-NWZGSLPU.js';
4
4
  import { Skeleton } from '../chunk-XBBEX4SF.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bearmenu/ui",
3
- "version": "0.8.54",
3
+ "version": "0.8.55",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "description": "BearMenu design system \u2014 shared UI tokens, primitives, and components",
6
6
  "license": "UNLICENSED",
@@ -109,3 +109,25 @@ describe("MentionSignal count glyph", () => {
109
109
  );
110
110
  });
111
111
  });
112
+
113
+ // Where reviews' praise is the point (the map's lists), the price is a fact a
114
+ // person checks, not the row's headline: it steps down to secondary text and
115
+ // the praise count keeps the bold accent (HIG review, 2026-11-08).
116
+ describe("MenuItem emphasis", () => {
117
+ it("keeps the price bold by default", () => {
118
+ render(<MenuItem item={wine} labels={labels} formatPrice={String} onOpen={() => {}} />);
119
+ const price = screen.getByText("169");
120
+ expect(price.className).toContain("font-bold");
121
+ expect(price.className).toContain("text-foreground");
122
+ });
123
+
124
+ it("sets the price as secondary text when praise leads", () => {
125
+ render(
126
+ <MenuItem item={wine} labels={labels} formatPrice={String} emphasis="praise" onOpen={() => {}} />
127
+ );
128
+ const price = screen.getByText("169");
129
+ expect(price.className).not.toContain("font-bold");
130
+ expect(price.className).toContain("text-muted-foreground");
131
+ expect(price.className).toContain("text-[13px]");
132
+ });
133
+ });
@@ -111,6 +111,14 @@ export type MenuItemProps = {
111
111
  * Defaults to `"mentions"`. Keep `labels.mentions` in step with it.
112
112
  */
113
113
  countKind?: MenuItemCountKind;
114
+ /**
115
+ * What the meta row leads with. `"price"` (default): the price is bold, the
116
+ * count's peer at the other end. `"praise"`: on surfaces where reviews'
117
+ * praise is the point (the map's lists), the price steps down to secondary
118
+ * text, a fact to check rather than the row's headline, and the praise count
119
+ * keeps the bold accent.
120
+ */
121
+ emphasis?: "price" | "praise";
114
122
  /**
115
123
  * An outside aggregate score for the item (a wine's Vivino rating, a beer's
116
124
  * Untappd one), shown as a star and the score beside the mention count.
@@ -259,6 +267,7 @@ export const MenuItem = memo(function MenuItem({
259
267
  onSignatureEvidence,
260
268
  onMentions,
261
269
  countKind = "mentions",
270
+ emphasis = "price",
262
271
  externalRating,
263
272
  formatRating = (score) => score.toFixed(1),
264
273
  markGradient,
@@ -448,7 +457,14 @@ export const MenuItem = memo(function MenuItem({
448
457
  <div className="flex min-w-0 items-baseline gap-x-2">
449
458
  {minPrice != null && (
450
459
  <span className="flex items-baseline gap-1">
451
- <span className="text-[15px] font-bold tabular-nums text-foreground">
460
+ <span
461
+ className={cn(
462
+ "tabular-nums",
463
+ emphasis === "praise"
464
+ ? "text-[13px] font-medium text-muted-foreground"
465
+ : "text-[15px] font-bold text-foreground"
466
+ )}
467
+ >
452
468
  {isPriceRange
453
469
  ? `${formatPrice(minPrice)}–${formatPrice(maxPrice!)}`
454
470
  : formatPrice(minPrice)}
@@ -169,3 +169,17 @@ describe("Tabs", () => {
169
169
  expect(screen.getByText("Panel A")).toBeInTheDocument();
170
170
  });
171
171
  });
172
+
173
+ // In dark mode `bg-background` is darker than the pill track (`bg-muted`), so
174
+ // the selected segment read as a hole; it lifts to the raised surface there.
175
+ describe("pill indicator in dark mode", () => {
176
+ it("raises the selected segment above the track", () => {
177
+ const { container } = render(<Triplet />);
178
+ const indicators = container.querySelectorAll('[role="tab"][data-state="active"] > span[aria-hidden]');
179
+ expect(indicators.length).toBeGreaterThan(0);
180
+ indicators.forEach((indicator) => {
181
+ expect(indicator.className).toContain("dark:bg-surface-2");
182
+ expect(indicator.className).toContain("dark:ring-1");
183
+ });
184
+ });
185
+ });
@@ -153,7 +153,10 @@ const TabsTrigger = React.forwardRef<
153
153
  transition={archetype.indicatorMorph}
154
154
  className={cn(
155
155
  "absolute inset-0 -z-0 motion-reduce:hidden",
156
- variant === "pill" && "rounded-xl bg-background shadow-sm",
156
+ // Dark: `bg-background` is darker than the `bg-muted` track, so the
157
+ // selection read as a hole; it lifts to the raised surface instead.
158
+ variant === "pill" &&
159
+ "rounded-xl bg-background shadow-sm dark:bg-surface-2 dark:ring-1 dark:ring-border",
157
160
  variant === "underline" && orientation === "horizontal" && "border-b-2 border-primary",
158
161
  variant === "underline" && orientation === "vertical" && "border-r-2 border-primary"
159
162
  )}
@@ -165,7 +168,10 @@ const TabsTrigger = React.forwardRef<
165
168
  aria-hidden
166
169
  className={cn(
167
170
  "absolute inset-0 -z-0 hidden motion-reduce:block",
168
- variant === "pill" && "rounded-xl bg-background shadow-sm",
171
+ // Dark: `bg-background` is darker than the `bg-muted` track, so the
172
+ // selection read as a hole; it lifts to the raised surface instead.
173
+ variant === "pill" &&
174
+ "rounded-xl bg-background shadow-sm dark:bg-surface-2 dark:ring-1 dark:ring-border",
169
175
  variant === "underline" && orientation === "horizontal" && "border-b-2 border-primary",
170
176
  variant === "underline" && orientation === "vertical" && "border-r-2 border-primary"
171
177
  )}