@oxyhq/bloom 0.35.7 → 0.35.8

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.
@@ -22,11 +22,12 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
22
22
  * scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
23
23
  * surface wrapping its content. The `framed` prop still drives whether that
24
24
  * surface is rounded + bordered, tri-state and resolved with pure NativeWind
25
- * (`md:` evaluates against the window width on native too — no JS breakpoint
26
- * hook needed):
25
+ * (the breakpoint's `min-*:` variant evaluates against the window width on
26
+ * native too — no JS breakpoint hook needed):
27
27
  *
28
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
29
- * bordered at `md`+ (`md:rounded-radius-28 md:border md:border-border`).
28
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
29
+ * breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
30
+ * (e.g. `md:rounded-radius-28 md:border md:border-border`).
30
31
  * - `false` → never framed (plain full-bleed at every size).
31
32
  * - `true` → always rounded + bordered.
32
33
  *
@@ -50,9 +51,34 @@ const GUTTER_MASK_SPREAD = exports.GUTTER_MASK_SPREAD = 40;
50
51
  const PANEL_TOP_INSET = exports.PANEL_TOP_INSET = 8;
51
52
  /** Bottom sticky inset (px) of the framed panel chrome. */
52
53
  const PANEL_BOTTOM_INSET = exports.PANEL_BOTTOM_INSET = 8;
54
+
55
+ /**
56
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
57
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
58
+ * Tailwind class bundle — `768` (default) uses the named `md:` screen
59
+ * (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
60
+ * `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
61
+ * 500px). The breakpoint tokens are never built at runtime, so a consumer's
62
+ * Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
63
+ */
64
+
65
+ /**
66
+ * Per-breakpoint literal Tailwind surface class bundle for the NATIVE responsive
67
+ * mode. Each value is a WHOLE literal string (never assembled from parts at
68
+ * runtime) so the Tailwind content-scan over `src/**`/`lib/**` picks up every
69
+ * `min-*`/`sm:`/`md:`/`lg:` token verbatim. Selecting a bundle by `framedFrom`
70
+ * value is the only runtime decision.
71
+ */
72
+ const RESPONSIVE_SURFACE = {
73
+ 500: 'min-[500px]:overflow-hidden min-[500px]:rounded-radius-28 min-[500px]:border min-[500px]:border-border',
74
+ 640: 'sm:overflow-hidden sm:rounded-radius-28 sm:border sm:border-border',
75
+ 768: 'md:overflow-hidden md:rounded-radius-28 md:border md:border-border',
76
+ 1024: 'lg:overflow-hidden lg:rounded-radius-28 lg:border lg:border-border'
77
+ };
53
78
  const ContentPanelComponent = ({
54
79
  children,
55
80
  framed,
81
+ framedFrom = 768,
56
82
  surfaceClassName,
57
83
  surfaceStyle,
58
84
  contentClassName,
@@ -61,11 +87,13 @@ const ContentPanelComponent = ({
61
87
  // Dev-only invariant — a ContentPanel must never be nested inside another.
62
88
  (0, _nestingContext.useContentPanelNestingGuard)();
63
89
 
64
- // Tri-state: `undefined` → responsive (md:-gated), `true` → always framed,
65
- // `false` → never framed (plain full-bleed). Whole literal class strings are
66
- // selected per mode so the Tailwind content-scan over `src/**` picks up every
67
- // `md:` / `rounded-radius-28` token verbatim (no dynamic concatenation).
68
- const surfaceBase = framed === undefined ? 'flex-1 md:overflow-hidden md:rounded-radius-28 md:border md:border-border' : framed ? 'flex-1 overflow-hidden rounded-radius-28 border border-border' : 'flex-1';
90
+ // Tri-state: `undefined` → responsive (breakpoint-gated), `true` → always
91
+ // framed, `false` → never framed (plain full-bleed). Whole literal class
92
+ // strings are selected per mode so the Tailwind content-scan over `src/**`
93
+ // picks up every `min-*`/`sm:`/`md:`/`lg:` / `rounded-radius-28` token verbatim
94
+ // (the breakpoint bundle lives whole in `RESPONSIVE_SURFACE`; `framed ===
95
+ // true`/`false` ignore `framedFrom` for fixed always/never framing).
96
+ const surfaceBase = framed === undefined ? `flex-1 ${RESPONSIVE_SURFACE[framedFrom]}` : framed ? 'flex-1 overflow-hidden rounded-radius-28 border border-border' : 'flex-1';
69
97
  const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
70
98
  const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
71
99
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_nestingContext.ContentPanelNestingContext.Provider, {
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_nestingContext","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","useContentPanelNestingGuard","surfaceBase","undefined","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","View","className","style","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;;;;;AA+BA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAEA,IAAAE,eAAA,GAAAF,OAAA;AAG2B,IAAAG,WAAA,GAAAH,OAAA;AAAA,SAAAD,wBAAAK,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAP,uBAAA,YAAAA,CAAAK,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AArC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA;AACA;AACA;AACA;AACO,MAAMkB,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAAG,EAAE;AACpC;AACO,MAAME,eAAe,GAAAD,OAAA,CAAAC,eAAA,GAAG,CAAC;AAChC;AACO,MAAMC,kBAAkB,GAAAF,OAAA,CAAAE,kBAAA,GAAG,CAAC;AA+BnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ;EACA,IAAAC,2CAA2B,EAAC,CAAC;;EAE7B;EACA;EACA;EACA;EACA,MAAMC,WAAW,GACfN,MAAM,KAAKO,SAAS,GAChB,2EAA2E,GAC3EP,MAAM,GACJ,+DAA+D,GAC/D,QAAQ;EAChB,MAAMQ,YAAY,GAAG,CAACF,WAAW,EAAEL,gBAAgB,IAAI,SAAS,CAAC,CAACQ,IAAI,CAAC,GAAG,CAAC;EAC3E,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEP,gBAAgB,CAAC,CAACQ,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACE,IAAAnC,WAAA,CAAAuC,GAAA,EAACxC,eAAA,CAAAyC,0BAA0B,CAACC,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAAjB,QAAA,eAC/C,IAAAzB,WAAA,CAAAuC,GAAA,EAACzC,YAAA,CAAA6C,IAAI;MACIC,SAAS,EAAEV,YAAY;MAC9BW,KAAK,EAAEjB,YAAa;MAAAH,QAAA,eAEpB,IAAAzB,WAAA,CAAAuC,GAAA,EAACzC,YAAA,CAAA6C,IAAI;QACIC,SAAS,EAAER,YAAY;QAC9BS,KAAK,EAAEf,YAAa;QAAAL,QAAA,EAEnBA;MAAQ,CACL;IAAC,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAEM,MAAMqB,YAAY,GAAAzB,OAAA,CAAAyB,YAAA,gBAAG,IAAAC,WAAI,EAACvB,qBAAqB,CAAC;AACvDsB,YAAY,CAACE,WAAW,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_nestingContext","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","RESPONSIVE_SURFACE","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","useContentPanelNestingGuard","surfaceBase","undefined","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","View","className","style","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;;;;;AAgCA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAEA,IAAAE,eAAA,GAAAF,OAAA;AAG2B,IAAAG,WAAA,GAAAH,OAAA;AAAA,SAAAD,wBAAAK,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAP,uBAAA,YAAAA,CAAAK,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AAtC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA;AACA;AACA;AACA;AACO,MAAMkB,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAAG,EAAE;AACpC;AACO,MAAME,eAAe,GAAAD,OAAA,CAAAC,eAAA,GAAG,CAAC;AAChC;AACO,MAAMC,kBAAkB,GAAAF,OAAA,CAAAE,kBAAA,GAAG,CAAC;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAuCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAgE,GAAG;EACvE,GAAG,EAAE,wGAAwG;EAC7G,GAAG,EAAE,oEAAoE;EACzE,GAAG,EAAE,oEAAoE;EACzE,IAAI,EAAE;AACR,CAAC;AAED,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,UAAU,GAAG,GAAG;EAChBC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ;EACA,IAAAC,2CAA2B,EAAC,CAAC;;EAE7B;EACA;EACA;EACA;EACA;EACA;EACA,MAAMC,WAAW,GACfP,MAAM,KAAKQ,SAAS,GAChB,UAAUX,kBAAkB,CAACI,UAAU,CAAC,EAAE,GAC1CD,MAAM,GACJ,+DAA+D,GAC/D,QAAQ;EAChB,MAAMS,YAAY,GAAG,CAACF,WAAW,EAAEL,gBAAgB,IAAI,SAAS,CAAC,CAACQ,IAAI,CAAC,GAAG,CAAC;EAC3E,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEP,gBAAgB,CAAC,CAACQ,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACE,IAAArC,WAAA,CAAAyC,GAAA,EAAC1C,eAAA,CAAA2C,0BAA0B,CAACC,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAAlB,QAAA,eAC/C,IAAA1B,WAAA,CAAAyC,GAAA,EAAC3C,YAAA,CAAA+C,IAAI;MACIC,SAAS,EAAEV,YAAY;MAC9BW,KAAK,EAAEjB,YAAa;MAAAJ,QAAA,eAEpB,IAAA1B,WAAA,CAAAyC,GAAA,EAAC3C,YAAA,CAAA+C,IAAI;QACIC,SAAS,EAAER,YAAY;QAC9BS,KAAK,EAAEf,YAAa;QAAAN,QAAA,EAEnBA;MAAQ,CACL;IAAC,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAEM,MAAMsB,YAAY,GAAA3B,OAAA,CAAA2B,YAAA,gBAAG,IAAAC,WAAI,EAACxB,qBAAqB,CAAC;AACvDuB,YAAY,CAACE,WAAW,GAAG,cAAc","ignoreList":[]}
@@ -38,18 +38,22 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
38
38
  * Framing is tri-state via the `framed` prop:
39
39
  *
40
40
  * - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
41
- * below `md`, framed at `md`+. The rounding is `md:`-gated and both overlays
42
- * are always rendered but carry `max-md:hidden` (`display:none` below `md`),
43
- * so the breakpoint is decided in CSS no consumer JS breakpoint hook.
41
+ * below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
42
+ * configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
43
+ * rounding is gated by that breakpoint's `min-*:` variant and both overlays
44
+ * are always rendered but carry the matching `max-*:hidden` literal
45
+ * (`display:none` below the breakpoint), so the breakpoint is decided entirely
46
+ * in CSS — no consumer JS breakpoint hook.
44
47
  * - `false` → NEVER framed (full-bleed at every size): a single flat surface,
45
48
  * no rounding, no overlays.
46
49
  * - `true` → ALWAYS framed (rounded + overlays at every size).
47
50
  *
48
- * The overlays are gated by VISIBILITY (a `max-md:hidden` literal) rather than
49
- * by conditional mounting for the responsive case, so crossing the breakpoint
50
- * never remounts them. The content wrapper likewise keeps a stable `key` so
51
- * `{children}` reconciles in place across the breakpoint instead of remounting
52
- * (which would reset feed scroll / the virtualizer + refetch).
51
+ * The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
52
+ * breakpoint) rather than by conditional mounting for the responsive case, so
53
+ * crossing the breakpoint never remounts them. The content wrapper likewise
54
+ * keeps a stable `key` so `{children}` reconciles in place across the breakpoint
55
+ * instead of remounting (which would reset feed scroll / the virtualizer +
56
+ * refetch).
53
57
  *
54
58
  * Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
55
59
  * select this file via the `"browser"` export condition in `package.json`.
@@ -66,9 +70,58 @@ const GUTTER_MASK_SPREAD = exports.GUTTER_MASK_SPREAD = 40;
66
70
  const PANEL_TOP_INSET = exports.PANEL_TOP_INSET = 8;
67
71
  /** Bottom sticky inset (px) of the framed panel chrome. */
68
72
  const PANEL_BOTTOM_INSET = exports.PANEL_BOTTOM_INSET = 8;
73
+
74
+ /**
75
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
76
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
77
+ * Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
78
+ * (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
79
+ * `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
80
+ * variants (there is no named screen at 500px). The breakpoint tokens are never
81
+ * built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
82
+ * them verbatim.
83
+ */
84
+
85
+ /**
86
+ * Per-breakpoint literal Tailwind variant tokens for the WEB responsive mode.
87
+ * Each field is a WHOLE literal class string (never assembled from parts at
88
+ * runtime) so the consumer's content-scan over `lib/**` picks up every
89
+ * arbitrary/`min-*`/`max-*`/`web:` token verbatim. Selecting a bundle by
90
+ * `framedFrom` value is the only runtime decision:
91
+ * - `surface` → framing rounding on the surface at/above the breakpoint.
92
+ * - `content` → the same rounding + a web-only horizontal clip on the
93
+ * inner content wrapper (`web:<bp>:overflow-x-clip`, platform-first-then-
94
+ * breakpoint — matching the consumer convention `web:sm:flex` in `@mercaria/ui`).
95
+ * - `overlayHidden` → `display:none` for the sticky overlays BELOW the breakpoint.
96
+ * `min-[500px]:` (≥500) and `max-[500px]:` (<500) are exactly complementary
97
+ * (meet at 500, no overlap/gap), mirroring the named `md:`/`max-md:` pair.
98
+ */
99
+ const RESPONSIVE_WEB = {
100
+ 500: {
101
+ surface: 'min-[500px]:rounded-radius-28',
102
+ content: 'min-[500px]:rounded-radius-28 web:min-[500px]:overflow-x-clip',
103
+ overlayHidden: 'max-[500px]:hidden'
104
+ },
105
+ 640: {
106
+ surface: 'sm:rounded-radius-28',
107
+ content: 'sm:rounded-radius-28 web:sm:overflow-x-clip',
108
+ overlayHidden: 'max-sm:hidden'
109
+ },
110
+ 768: {
111
+ surface: 'md:rounded-radius-28',
112
+ content: 'md:rounded-radius-28 web:md:overflow-x-clip',
113
+ overlayHidden: 'max-md:hidden'
114
+ },
115
+ 1024: {
116
+ surface: 'lg:rounded-radius-28',
117
+ content: 'lg:rounded-radius-28 web:lg:overflow-x-clip',
118
+ overlayHidden: 'max-lg:hidden'
119
+ }
120
+ };
69
121
  const ContentPanelComponent = ({
70
122
  children,
71
123
  framed,
124
+ framedFrom = 768,
72
125
  surfaceClassName,
73
126
  surfaceStyle,
74
127
  contentClassName,
@@ -87,13 +140,16 @@ const ContentPanelComponent = ({
87
140
  // `false` → never framed (full-bleed).
88
141
  const responsive = framed === undefined;
89
142
  const showOverlays = framed !== false;
143
+ // Responsive mode selects a pre-shipped literal breakpoint bundle by value;
144
+ // `framed === true`/`false` ignore `framedFrom` (fixed always/never framing).
145
+ const bp = RESPONSIVE_WEB[framedFrom];
90
146
 
91
147
  // Whole literal class strings selected per mode (the Tailwind content-scan
92
- // over `lib/**` requires each arbitrary/`md:`/`web:` token stay literal).
93
- // `web:md:overflow-x-clip` is platform-first-then-breakpoint, matching the
94
- // consumer convention (`web:sm:flex` in `@mercaria/ui`).
95
- const surfaceBase = responsive ? 'flex-1 md:rounded-radius-28' : framed ? 'flex-1 rounded-radius-28' : 'flex-1';
96
- const contentBase = responsive ? 'flex-1 md:rounded-radius-28 web:md:overflow-x-clip' : framed ? 'flex-1 rounded-radius-28 web:overflow-x-clip' : 'flex-1';
148
+ // over `lib/**` requires each arbitrary/`min-*`/`max-*`/`web:` token stay
149
+ // literal — the breakpoint tokens live whole in `RESPONSIVE_WEB` above and are
150
+ // only spliced in, never assembled from parts).
151
+ const surfaceBase = responsive ? `flex-1 ${bp.surface}` : framed ? 'flex-1 rounded-radius-28' : 'flex-1';
152
+ const contentBase = responsive ? `flex-1 ${bp.content}` : framed ? 'flex-1 rounded-radius-28 web:overflow-x-clip' : 'flex-1';
97
153
  const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
98
154
  const contentClass = [contentBase, contentClassName].filter(Boolean).join(' ');
99
155
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_nestingContext.ContentPanelNestingContext.Provider, {
@@ -103,13 +159,13 @@ const ContentPanelComponent = ({
103
159
  style: surfaceStyle,
104
160
  children: [showOverlays && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
105
161
  pointerEvents: "none",
106
- className: responsive ? 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 max-md:hidden web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]' : 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
162
+ className: responsive ? `web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]` : 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
107
163
  style: {
108
164
  boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
109
165
  }
110
166
  }, "bleed-mask"), showOverlays && showStickyFrame !== false && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
111
167
  pointerEvents: "none",
112
- className: responsive ? 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border max-md:hidden web:[margin-bottom:calc(-100dvh+16px)]' : 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
168
+ className: responsive ? `web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)]` : 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
113
169
  }, "border-frame"), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
114
170
  className: contentClass,
115
171
  style: contentStyle,
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_nestingContext","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","useContentPanelNestingGuard","colors","useTheme","responsive","undefined","showOverlays","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","jsxs","View","className","style","pointerEvents","boxShadow","background","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_nestingContext","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","RESPONSIVE_WEB","surface","content","overlayHidden","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","useContentPanelNestingGuard","colors","useTheme","responsive","undefined","showOverlays","bp","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","jsxs","View","className","style","pointerEvents","boxShadow","background","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":"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","ignoreList":[]}
@@ -13,11 +13,12 @@
13
13
  * scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
14
14
  * surface wrapping its content. The `framed` prop still drives whether that
15
15
  * surface is rounded + bordered, tri-state and resolved with pure NativeWind
16
- * (`md:` evaluates against the window width on native too — no JS breakpoint
17
- * hook needed):
16
+ * (the breakpoint's `min-*:` variant evaluates against the window width on
17
+ * native too — no JS breakpoint hook needed):
18
18
  *
19
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
20
- * bordered at `md`+ (`md:rounded-radius-28 md:border md:border-border`).
19
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
20
+ * breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
21
+ * (e.g. `md:rounded-radius-28 md:border md:border-border`).
21
22
  * - `false` → never framed (plain full-bleed at every size).
22
23
  * - `true` → always rounded + bordered.
23
24
  *
@@ -45,9 +46,34 @@ export const GUTTER_MASK_SPREAD = 40;
45
46
  export const PANEL_TOP_INSET = 8;
46
47
  /** Bottom sticky inset (px) of the framed panel chrome. */
47
48
  export const PANEL_BOTTOM_INSET = 8;
49
+
50
+ /**
51
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
52
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
53
+ * Tailwind class bundle — `768` (default) uses the named `md:` screen
54
+ * (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
55
+ * `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
56
+ * 500px). The breakpoint tokens are never built at runtime, so a consumer's
57
+ * Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
58
+ */
59
+
60
+ /**
61
+ * Per-breakpoint literal Tailwind surface class bundle for the NATIVE responsive
62
+ * mode. Each value is a WHOLE literal string (never assembled from parts at
63
+ * runtime) so the Tailwind content-scan over `src/**`/`lib/**` picks up every
64
+ * `min-*`/`sm:`/`md:`/`lg:` token verbatim. Selecting a bundle by `framedFrom`
65
+ * value is the only runtime decision.
66
+ */
67
+ const RESPONSIVE_SURFACE = {
68
+ 500: 'min-[500px]:overflow-hidden min-[500px]:rounded-radius-28 min-[500px]:border min-[500px]:border-border',
69
+ 640: 'sm:overflow-hidden sm:rounded-radius-28 sm:border sm:border-border',
70
+ 768: 'md:overflow-hidden md:rounded-radius-28 md:border md:border-border',
71
+ 1024: 'lg:overflow-hidden lg:rounded-radius-28 lg:border lg:border-border'
72
+ };
48
73
  const ContentPanelComponent = ({
49
74
  children,
50
75
  framed,
76
+ framedFrom = 768,
51
77
  surfaceClassName,
52
78
  surfaceStyle,
53
79
  contentClassName,
@@ -56,11 +82,13 @@ const ContentPanelComponent = ({
56
82
  // Dev-only invariant — a ContentPanel must never be nested inside another.
57
83
  useContentPanelNestingGuard();
58
84
 
59
- // Tri-state: `undefined` → responsive (md:-gated), `true` → always framed,
60
- // `false` → never framed (plain full-bleed). Whole literal class strings are
61
- // selected per mode so the Tailwind content-scan over `src/**` picks up every
62
- // `md:` / `rounded-radius-28` token verbatim (no dynamic concatenation).
63
- const surfaceBase = framed === undefined ? 'flex-1 md:overflow-hidden md:rounded-radius-28 md:border md:border-border' : framed ? 'flex-1 overflow-hidden rounded-radius-28 border border-border' : 'flex-1';
85
+ // Tri-state: `undefined` → responsive (breakpoint-gated), `true` → always
86
+ // framed, `false` → never framed (plain full-bleed). Whole literal class
87
+ // strings are selected per mode so the Tailwind content-scan over `src/**`
88
+ // picks up every `min-*`/`sm:`/`md:`/`lg:` / `rounded-radius-28` token verbatim
89
+ // (the breakpoint bundle lives whole in `RESPONSIVE_SURFACE`; `framed ===
90
+ // true`/`false` ignore `framedFrom` for fixed always/never framing).
91
+ const surfaceBase = framed === undefined ? `flex-1 ${RESPONSIVE_SURFACE[framedFrom]}` : framed ? 'flex-1 overflow-hidden rounded-radius-28 border border-border' : 'flex-1';
64
92
  const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
65
93
  const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
66
94
  return /*#__PURE__*/_jsx(ContentPanelNestingContext.Provider, {
@@ -1 +1 @@
1
- {"version":3,"names":["React","memo","View","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","surfaceBase","undefined","surfaceClass","join","contentClass","filter","Boolean","Provider","value","className","style","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AACnC,SAASC,IAAI,QAAwC,cAAc;AAEnE,SACEC,0BAA0B,EAC1BC,2BAA2B,QACtB,sBAAmB;;AAE1B;AACA;AACA;AACA;AAHA,SAAAC,GAAA,IAAAC,IAAA;AAIA,OAAO,MAAMC,kBAAkB,GAAG,EAAE;AACpC;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC;AAChC;AACA,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AA+BnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ;EACAZ,2BAA2B,CAAC,CAAC;;EAE7B;EACA;EACA;EACA;EACA,MAAMa,WAAW,GACfL,MAAM,KAAKM,SAAS,GAChB,2EAA2E,GAC3EN,MAAM,GACJ,+DAA+D,GAC/D,QAAQ;EAChB,MAAMO,YAAY,GAAG,CAACF,WAAW,EAAEJ,gBAAgB,IAAI,SAAS,CAAC,CAACO,IAAI,CAAC,GAAG,CAAC;EAC3E,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEN,gBAAgB,CAAC,CAACO,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACEd,IAAA,CAACH,0BAA0B,CAACqB,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAAd,QAAA,eAC/CL,IAAA,CAACJ,IAAI;MACIwB,SAAS,EAAEP,YAAY;MAC9BQ,KAAK,EAAEb,YAAa;MAAAH,QAAA,eAEpBL,IAAA,CAACJ,IAAI;QACIwB,SAAS,EAAEL,YAAY;QAC9BM,KAAK,EAAEX,YAAa;QAAAL,QAAA,EAEnBA;MAAQ,CACL;IAAC,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAED,OAAO,MAAMiB,YAAY,gBAAG3B,IAAI,CAACS,qBAAqB,CAAC;AACvDkB,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["React","memo","View","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","RESPONSIVE_SURFACE","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","surfaceBase","undefined","surfaceClass","join","contentClass","filter","Boolean","Provider","value","className","style","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AACnC,SAASC,IAAI,QAAwC,cAAc;AAEnE,SACEC,0BAA0B,EAC1BC,2BAA2B,QACtB,sBAAmB;;AAE1B;AACA;AACA;AACA;AAHA,SAAAC,GAAA,IAAAC,IAAA;AAIA,OAAO,MAAMC,kBAAkB,GAAG,EAAE;AACpC;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC;AAChC;AACA,OAAO,MAAMC,kBAAkB,GAAG,CAAC;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAuCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAgE,GAAG;EACvE,GAAG,EAAE,wGAAwG;EAC7G,GAAG,EAAE,oEAAoE;EACzE,GAAG,EAAE,oEAAoE;EACzE,IAAI,EAAE;AACR,CAAC;AAED,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,UAAU,GAAG,GAAG;EAChBC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ;EACAd,2BAA2B,CAAC,CAAC;;EAE7B;EACA;EACA;EACA;EACA;EACA;EACA,MAAMe,WAAW,GACfN,MAAM,KAAKO,SAAS,GAChB,UAAUV,kBAAkB,CAACI,UAAU,CAAC,EAAE,GAC1CD,MAAM,GACJ,+DAA+D,GAC/D,QAAQ;EAChB,MAAMQ,YAAY,GAAG,CAACF,WAAW,EAAEJ,gBAAgB,IAAI,SAAS,CAAC,CAACO,IAAI,CAAC,GAAG,CAAC;EAC3E,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEN,gBAAgB,CAAC,CAACO,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACEhB,IAAA,CAACH,0BAA0B,CAACuB,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAAf,QAAA,eAC/CN,IAAA,CAACJ,IAAI;MACI0B,SAAS,EAAEP,YAAY;MAC9BQ,KAAK,EAAEb,YAAa;MAAAJ,QAAA,eAEpBN,IAAA,CAACJ,IAAI;QACI0B,SAAS,EAAEL,YAAY;QAC9BM,KAAK,EAAEX,YAAa;QAAAN,QAAA,EAEnBA;MAAQ,CACL;IAAC,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAED,OAAO,MAAMkB,YAAY,gBAAG7B,IAAI,CAACU,qBAAqB,CAAC;AACvDmB,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
@@ -28,18 +28,22 @@
28
28
  * Framing is tri-state via the `framed` prop:
29
29
  *
30
30
  * - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
31
- * below `md`, framed at `md`+. The rounding is `md:`-gated and both overlays
32
- * are always rendered but carry `max-md:hidden` (`display:none` below `md`),
33
- * so the breakpoint is decided in CSS no consumer JS breakpoint hook.
31
+ * below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
32
+ * configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
33
+ * rounding is gated by that breakpoint's `min-*:` variant and both overlays
34
+ * are always rendered but carry the matching `max-*:hidden` literal
35
+ * (`display:none` below the breakpoint), so the breakpoint is decided entirely
36
+ * in CSS — no consumer JS breakpoint hook.
34
37
  * - `false` → NEVER framed (full-bleed at every size): a single flat surface,
35
38
  * no rounding, no overlays.
36
39
  * - `true` → ALWAYS framed (rounded + overlays at every size).
37
40
  *
38
- * The overlays are gated by VISIBILITY (a `max-md:hidden` literal) rather than
39
- * by conditional mounting for the responsive case, so crossing the breakpoint
40
- * never remounts them. The content wrapper likewise keeps a stable `key` so
41
- * `{children}` reconciles in place across the breakpoint instead of remounting
42
- * (which would reset feed scroll / the virtualizer + refetch).
41
+ * The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
42
+ * breakpoint) rather than by conditional mounting for the responsive case, so
43
+ * crossing the breakpoint never remounts them. The content wrapper likewise
44
+ * keeps a stable `key` so `{children}` reconciles in place across the breakpoint
45
+ * instead of remounting (which would reset feed scroll / the virtualizer +
46
+ * refetch).
43
47
  *
44
48
  * Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
45
49
  * select this file via the `"browser"` export condition in `package.json`.
@@ -61,9 +65,58 @@ export const GUTTER_MASK_SPREAD = 40;
61
65
  export const PANEL_TOP_INSET = 8;
62
66
  /** Bottom sticky inset (px) of the framed panel chrome. */
63
67
  export const PANEL_BOTTOM_INSET = 8;
68
+
69
+ /**
70
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
71
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
72
+ * Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
73
+ * (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
74
+ * `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
75
+ * variants (there is no named screen at 500px). The breakpoint tokens are never
76
+ * built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
77
+ * them verbatim.
78
+ */
79
+
80
+ /**
81
+ * Per-breakpoint literal Tailwind variant tokens for the WEB responsive mode.
82
+ * Each field is a WHOLE literal class string (never assembled from parts at
83
+ * runtime) so the consumer's content-scan over `lib/**` picks up every
84
+ * arbitrary/`min-*`/`max-*`/`web:` token verbatim. Selecting a bundle by
85
+ * `framedFrom` value is the only runtime decision:
86
+ * - `surface` → framing rounding on the surface at/above the breakpoint.
87
+ * - `content` → the same rounding + a web-only horizontal clip on the
88
+ * inner content wrapper (`web:<bp>:overflow-x-clip`, platform-first-then-
89
+ * breakpoint — matching the consumer convention `web:sm:flex` in `@mercaria/ui`).
90
+ * - `overlayHidden` → `display:none` for the sticky overlays BELOW the breakpoint.
91
+ * `min-[500px]:` (≥500) and `max-[500px]:` (<500) are exactly complementary
92
+ * (meet at 500, no overlap/gap), mirroring the named `md:`/`max-md:` pair.
93
+ */
94
+ const RESPONSIVE_WEB = {
95
+ 500: {
96
+ surface: 'min-[500px]:rounded-radius-28',
97
+ content: 'min-[500px]:rounded-radius-28 web:min-[500px]:overflow-x-clip',
98
+ overlayHidden: 'max-[500px]:hidden'
99
+ },
100
+ 640: {
101
+ surface: 'sm:rounded-radius-28',
102
+ content: 'sm:rounded-radius-28 web:sm:overflow-x-clip',
103
+ overlayHidden: 'max-sm:hidden'
104
+ },
105
+ 768: {
106
+ surface: 'md:rounded-radius-28',
107
+ content: 'md:rounded-radius-28 web:md:overflow-x-clip',
108
+ overlayHidden: 'max-md:hidden'
109
+ },
110
+ 1024: {
111
+ surface: 'lg:rounded-radius-28',
112
+ content: 'lg:rounded-radius-28 web:lg:overflow-x-clip',
113
+ overlayHidden: 'max-lg:hidden'
114
+ }
115
+ };
64
116
  const ContentPanelComponent = ({
65
117
  children,
66
118
  framed,
119
+ framedFrom = 768,
67
120
  surfaceClassName,
68
121
  surfaceStyle,
69
122
  contentClassName,
@@ -82,13 +135,16 @@ const ContentPanelComponent = ({
82
135
  // `false` → never framed (full-bleed).
83
136
  const responsive = framed === undefined;
84
137
  const showOverlays = framed !== false;
138
+ // Responsive mode selects a pre-shipped literal breakpoint bundle by value;
139
+ // `framed === true`/`false` ignore `framedFrom` (fixed always/never framing).
140
+ const bp = RESPONSIVE_WEB[framedFrom];
85
141
 
86
142
  // Whole literal class strings selected per mode (the Tailwind content-scan
87
- // over `lib/**` requires each arbitrary/`md:`/`web:` token stay literal).
88
- // `web:md:overflow-x-clip` is platform-first-then-breakpoint, matching the
89
- // consumer convention (`web:sm:flex` in `@mercaria/ui`).
90
- const surfaceBase = responsive ? 'flex-1 md:rounded-radius-28' : framed ? 'flex-1 rounded-radius-28' : 'flex-1';
91
- const contentBase = responsive ? 'flex-1 md:rounded-radius-28 web:md:overflow-x-clip' : framed ? 'flex-1 rounded-radius-28 web:overflow-x-clip' : 'flex-1';
143
+ // over `lib/**` requires each arbitrary/`min-*`/`max-*`/`web:` token stay
144
+ // literal — the breakpoint tokens live whole in `RESPONSIVE_WEB` above and are
145
+ // only spliced in, never assembled from parts).
146
+ const surfaceBase = responsive ? `flex-1 ${bp.surface}` : framed ? 'flex-1 rounded-radius-28' : 'flex-1';
147
+ const contentBase = responsive ? `flex-1 ${bp.content}` : framed ? 'flex-1 rounded-radius-28 web:overflow-x-clip' : 'flex-1';
92
148
  const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
93
149
  const contentClass = [contentBase, contentClassName].filter(Boolean).join(' ');
94
150
  return /*#__PURE__*/_jsx(ContentPanelNestingContext.Provider, {
@@ -98,13 +154,13 @@ const ContentPanelComponent = ({
98
154
  style: surfaceStyle,
99
155
  children: [showOverlays && /*#__PURE__*/_jsx(View, {
100
156
  pointerEvents: "none",
101
- className: responsive ? 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 max-md:hidden web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]' : 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
157
+ className: responsive ? `web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]` : 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
102
158
  style: {
103
159
  boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
104
160
  }
105
161
  }, "bleed-mask"), showOverlays && showStickyFrame !== false && /*#__PURE__*/_jsx(View, {
106
162
  pointerEvents: "none",
107
- className: responsive ? 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border max-md:hidden web:[margin-bottom:calc(-100dvh+16px)]' : 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
163
+ className: responsive ? `web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)]` : 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
108
164
  }, "border-frame"), /*#__PURE__*/_jsx(View, {
109
165
  className: contentClass,
110
166
  style: contentStyle,
@@ -1 +1 @@
1
- {"version":3,"names":["React","memo","View","useTheme","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","jsxs","_jsxs","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","colors","responsive","undefined","showOverlays","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","Provider","value","className","style","pointerEvents","boxShadow","background","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AACnC,SAASC,IAAI,QAAwC,cAAc;AAEnE,SAASC,QAAQ,QAAQ,uBAAoB;AAC7C,SACEC,0BAA0B,EAC1BC,2BAA2B,QACtB,sBAAmB;;AAE1B;AAAA,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AACA,OAAO,MAAMC,kBAAkB,GAAG,EAAE;AACpC;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC;AAChC;AACA,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AAgCnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC,YAAY;EACZC,eAAe;EACfC;AACF,CAAC,KAAK;EACJ;EACA;EACAhB,2BAA2B,CAAC,CAAC;EAC7B,MAAM;IAAEiB;EAAO,CAAC,GAAGnB,QAAQ,CAAC,CAAC;;EAE7B;EACA;EACA,MAAMoB,UAAU,GAAGR,MAAM,KAAKS,SAAS;EACvC,MAAMC,YAAY,GAAGV,MAAM,KAAK,KAAK;;EAErC;EACA;EACA;EACA;EACA,MAAMW,WAAW,GAAGH,UAAU,GAC1B,6BAA6B,GAC7BR,MAAM,GACJ,0BAA0B,GAC1B,QAAQ;EACd,MAAMY,WAAW,GAAGJ,UAAU,GAC1B,oDAAoD,GACpDR,MAAM,GACJ,8CAA8C,GAC9C,QAAQ;EACd,MAAMa,YAAY,GAAG,CAACF,WAAW,EAAEV,gBAAgB,IAAI,SAAS,CAAC,CAACa,IAAI,CAAC,GAAG,CAAC;EAC3E,MAAMC,YAAY,GAAG,CAACH,WAAW,EAAET,gBAAgB,CAAC,CAACa,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE9E,oBACEtB,IAAA,CAACH,0BAA0B,CAAC6B,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAApB,QAAA,eAC/CL,KAAA,CAACP,IAAI;MACIiC,SAAS,EAAEP,YAAY;MAC9BQ,KAAK,EAAEnB,YAAa;MAAAH,QAAA,GAKnBW,YAAY,iBACXlB,IAAA,CAACL,IAAI;QAEHmC,aAAa,EAAC,MAAM;QAElBF,SAAS,EAAEZ,UAAU,GACjB,4JAA4J,GAC5J,8IAA8I;QAEpJa,KAAK,EAAE;UAAEE,SAAS,EAAE,SAAS5B,kBAAkB,MAAMW,SAAS,IAAIC,MAAM,CAACiB,UAAU;QAAG;MAAE,GAPpF,YAQL,CACF,EAGAd,YAAY,IAAIL,eAAe,KAAK,KAAK,iBACxCb,IAAA,CAACL,IAAI;QAEHmC,aAAa,EAAC,MAAM;QAElBF,SAAS,EAAEZ,UAAU,GACjB,uJAAuJ,GACvJ;MAAyI,GAL3I,cAOL,CACF,eAKDhB,IAAA,CAACL,IAAI;QAEIiC,SAAS,EAAEL,YAAY;QAC9BM,KAAK,EAAEjB,YAAa;QAAAL,QAAA,EAEnBA;MAAQ,GAJL,SAKA,CAAC;IAAA,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAED,OAAO,MAAM0B,YAAY,gBAAGvC,IAAI,CAACY,qBAAqB,CAAC;AACvD2B,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["React","memo","View","useTheme","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","jsxs","_jsxs","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","RESPONSIVE_WEB","surface","content","overlayHidden","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","colors","responsive","undefined","showOverlays","bp","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","Provider","value","className","style","pointerEvents","boxShadow","background","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":"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","ignoreList":[]}
@@ -11,11 +11,12 @@
11
11
  * scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
12
12
  * surface wrapping its content. The `framed` prop still drives whether that
13
13
  * surface is rounded + bordered, tri-state and resolved with pure NativeWind
14
- * (`md:` evaluates against the window width on native too — no JS breakpoint
15
- * hook needed):
14
+ * (the breakpoint's `min-*:` variant evaluates against the window width on
15
+ * native too — no JS breakpoint hook needed):
16
16
  *
17
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
18
- * bordered at `md`+ (`md:rounded-radius-28 md:border md:border-border`).
17
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
18
+ * breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
19
+ * (e.g. `md:rounded-radius-28 md:border md:border-border`).
19
20
  * - `false` → never framed (plain full-bleed at every size).
20
21
  * - `true` → always rounded + bordered.
21
22
  *
@@ -40,17 +41,34 @@ export declare const GUTTER_MASK_SPREAD = 40;
40
41
  export declare const PANEL_TOP_INSET = 8;
41
42
  /** Bottom sticky inset (px) of the framed panel chrome. */
42
43
  export declare const PANEL_BOTTOM_INSET = 8;
44
+ /**
45
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
46
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
47
+ * Tailwind class bundle — `768` (default) uses the named `md:` screen
48
+ * (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
49
+ * `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
50
+ * 500px). The breakpoint tokens are never built at runtime, so a consumer's
51
+ * Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
52
+ */
53
+ export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
43
54
  export interface ContentPanelProps {
44
55
  children: React.ReactNode;
45
56
  /**
46
57
  * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
47
58
  * breakpoint hook needed:
48
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
49
- * bordered at `md`+.
59
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
60
+ * breakpoint, rounded + bordered at/above it.
50
61
  * - `false` → never framed (plain full-bleed at every size).
51
62
  * - `true` → always rounded + bordered.
52
63
  */
53
64
  framed?: boolean;
65
+ /**
66
+ * Viewport width at which the RESPONSIVE panel switches from full-bleed to
67
+ * framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
68
+ * when `framed` is `true` (always framed) or `false` (never framed). Defaults
69
+ * to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
70
+ */
71
+ framedFrom?: ContentPanelFramedBreakpoint;
54
72
  /** Override the surface background utility (defaults to `bg-card`). */
55
73
  surfaceClassName?: string;
56
74
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAOpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2CD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAOpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4DD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
@@ -26,18 +26,22 @@
26
26
  * Framing is tri-state via the `framed` prop:
27
27
  *
28
28
  * - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
29
- * below `md`, framed at `md`+. The rounding is `md:`-gated and both overlays
30
- * are always rendered but carry `max-md:hidden` (`display:none` below `md`),
31
- * so the breakpoint is decided in CSS no consumer JS breakpoint hook.
29
+ * below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
30
+ * configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
31
+ * rounding is gated by that breakpoint's `min-*:` variant and both overlays
32
+ * are always rendered but carry the matching `max-*:hidden` literal
33
+ * (`display:none` below the breakpoint), so the breakpoint is decided entirely
34
+ * in CSS — no consumer JS breakpoint hook.
32
35
  * - `false` → NEVER framed (full-bleed at every size): a single flat surface,
33
36
  * no rounding, no overlays.
34
37
  * - `true` → ALWAYS framed (rounded + overlays at every size).
35
38
  *
36
- * The overlays are gated by VISIBILITY (a `max-md:hidden` literal) rather than
37
- * by conditional mounting for the responsive case, so crossing the breakpoint
38
- * never remounts them. The content wrapper likewise keeps a stable `key` so
39
- * `{children}` reconciles in place across the breakpoint instead of remounting
40
- * (which would reset feed scroll / the virtualizer + refetch).
39
+ * The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
40
+ * breakpoint) rather than by conditional mounting for the responsive case, so
41
+ * crossing the breakpoint never remounts them. The content wrapper likewise
42
+ * keeps a stable `key` so `{children}` reconciles in place across the breakpoint
43
+ * instead of remounting (which would reset feed scroll / the virtualizer +
44
+ * refetch).
41
45
  *
42
46
  * Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
43
47
  * select this file via the `"browser"` export condition in `package.json`.
@@ -55,17 +59,36 @@ export declare const GUTTER_MASK_SPREAD = 40;
55
59
  export declare const PANEL_TOP_INSET = 8;
56
60
  /** Bottom sticky inset (px) of the framed panel chrome. */
57
61
  export declare const PANEL_BOTTOM_INSET = 8;
62
+ /**
63
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
64
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
65
+ * Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
66
+ * (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
67
+ * `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
68
+ * variants (there is no named screen at 500px). The breakpoint tokens are never
69
+ * built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
70
+ * them verbatim.
71
+ */
72
+ export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
58
73
  export interface ContentPanelProps {
59
74
  children: React.ReactNode;
60
75
  /**
61
76
  * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
62
77
  * breakpoint hook needed:
63
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, framed at
64
- * `md`+ (rounded corners + sticky bleed-mask/border overlays).
78
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
79
+ * breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
80
+ * overlays).
65
81
  * - `false` → never framed (full-bleed at every size).
66
82
  * - `true` → always framed (rounded + overlays at every size).
67
83
  */
68
84
  framed?: boolean;
85
+ /**
86
+ * Viewport width at which the RESPONSIVE panel switches from full-bleed to
87
+ * framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
88
+ * when `framed` is `true` (always framed) or `false` (never framed). Defaults
89
+ * to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
90
+ */
91
+ framedFrom?: ContentPanelFramedBreakpoint;
69
92
  /** Override the surface background utility (defaults to `bg-card`). */
70
93
  surfaceClassName?: string;
71
94
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyFD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqID,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
@@ -11,11 +11,12 @@
11
11
  * scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
12
12
  * surface wrapping its content. The `framed` prop still drives whether that
13
13
  * surface is rounded + bordered, tri-state and resolved with pure NativeWind
14
- * (`md:` evaluates against the window width on native too — no JS breakpoint
15
- * hook needed):
14
+ * (the breakpoint's `min-*:` variant evaluates against the window width on
15
+ * native too — no JS breakpoint hook needed):
16
16
  *
17
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
18
- * bordered at `md`+ (`md:rounded-radius-28 md:border md:border-border`).
17
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
18
+ * breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
19
+ * (e.g. `md:rounded-radius-28 md:border md:border-border`).
19
20
  * - `false` → never framed (plain full-bleed at every size).
20
21
  * - `true` → always rounded + bordered.
21
22
  *
@@ -40,17 +41,34 @@ export declare const GUTTER_MASK_SPREAD = 40;
40
41
  export declare const PANEL_TOP_INSET = 8;
41
42
  /** Bottom sticky inset (px) of the framed panel chrome. */
42
43
  export declare const PANEL_BOTTOM_INSET = 8;
44
+ /**
45
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
46
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
47
+ * Tailwind class bundle — `768` (default) uses the named `md:` screen
48
+ * (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
49
+ * `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
50
+ * 500px). The breakpoint tokens are never built at runtime, so a consumer's
51
+ * Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
52
+ */
53
+ export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
43
54
  export interface ContentPanelProps {
44
55
  children: React.ReactNode;
45
56
  /**
46
57
  * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
47
58
  * breakpoint hook needed:
48
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
49
- * bordered at `md`+.
59
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
60
+ * breakpoint, rounded + bordered at/above it.
50
61
  * - `false` → never framed (plain full-bleed at every size).
51
62
  * - `true` → always rounded + bordered.
52
63
  */
53
64
  framed?: boolean;
65
+ /**
66
+ * Viewport width at which the RESPONSIVE panel switches from full-bleed to
67
+ * framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
68
+ * when `framed` is `true` (always framed) or `false` (never framed). Defaults
69
+ * to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
70
+ */
71
+ framedFrom?: ContentPanelFramedBreakpoint;
54
72
  /** Override the surface background utility (defaults to `bg-card`). */
55
73
  surfaceClassName?: string;
56
74
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAOpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2CD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAOpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4DD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
@@ -26,18 +26,22 @@
26
26
  * Framing is tri-state via the `framed` prop:
27
27
  *
28
28
  * - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
29
- * below `md`, framed at `md`+. The rounding is `md:`-gated and both overlays
30
- * are always rendered but carry `max-md:hidden` (`display:none` below `md`),
31
- * so the breakpoint is decided in CSS no consumer JS breakpoint hook.
29
+ * below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
30
+ * configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
31
+ * rounding is gated by that breakpoint's `min-*:` variant and both overlays
32
+ * are always rendered but carry the matching `max-*:hidden` literal
33
+ * (`display:none` below the breakpoint), so the breakpoint is decided entirely
34
+ * in CSS — no consumer JS breakpoint hook.
32
35
  * - `false` → NEVER framed (full-bleed at every size): a single flat surface,
33
36
  * no rounding, no overlays.
34
37
  * - `true` → ALWAYS framed (rounded + overlays at every size).
35
38
  *
36
- * The overlays are gated by VISIBILITY (a `max-md:hidden` literal) rather than
37
- * by conditional mounting for the responsive case, so crossing the breakpoint
38
- * never remounts them. The content wrapper likewise keeps a stable `key` so
39
- * `{children}` reconciles in place across the breakpoint instead of remounting
40
- * (which would reset feed scroll / the virtualizer + refetch).
39
+ * The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
40
+ * breakpoint) rather than by conditional mounting for the responsive case, so
41
+ * crossing the breakpoint never remounts them. The content wrapper likewise
42
+ * keeps a stable `key` so `{children}` reconciles in place across the breakpoint
43
+ * instead of remounting (which would reset feed scroll / the virtualizer +
44
+ * refetch).
41
45
  *
42
46
  * Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
43
47
  * select this file via the `"browser"` export condition in `package.json`.
@@ -55,17 +59,36 @@ export declare const GUTTER_MASK_SPREAD = 40;
55
59
  export declare const PANEL_TOP_INSET = 8;
56
60
  /** Bottom sticky inset (px) of the framed panel chrome. */
57
61
  export declare const PANEL_BOTTOM_INSET = 8;
62
+ /**
63
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
64
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
65
+ * Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
66
+ * (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
67
+ * `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
68
+ * variants (there is no named screen at 500px). The breakpoint tokens are never
69
+ * built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
70
+ * them verbatim.
71
+ */
72
+ export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
58
73
  export interface ContentPanelProps {
59
74
  children: React.ReactNode;
60
75
  /**
61
76
  * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
62
77
  * breakpoint hook needed:
63
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, framed at
64
- * `md`+ (rounded corners + sticky bleed-mask/border overlays).
78
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
79
+ * breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
80
+ * overlays).
65
81
  * - `false` → never framed (full-bleed at every size).
66
82
  * - `true` → always framed (rounded + overlays at every size).
67
83
  */
68
84
  framed?: boolean;
85
+ /**
86
+ * Viewport width at which the RESPONSIVE panel switches from full-bleed to
87
+ * framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
88
+ * when `framed` is `true` (always framed) or `false` (never framed). Defaults
89
+ * to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
90
+ */
91
+ framedFrom?: ContentPanelFramedBreakpoint;
69
92
  /** Override the surface background utility (defaults to `bg-card`). */
70
93
  surfaceClassName?: string;
71
94
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyFD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqID,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.35.7",
3
+ "version": "0.35.8",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -4,6 +4,14 @@ import { render } from '@testing-library/react-native';
4
4
 
5
5
  import { ContentPanel } from '../content-panel';
6
6
 
7
+ /** Read the outer surface View's `className` from the rendered native tree. */
8
+ function surfaceClassName(node: ReturnType<typeof render>): string {
9
+ const json = node.toJSON();
10
+ const el = Array.isArray(json) ? json[0] : json;
11
+ const className = el?.props?.className;
12
+ return typeof className === 'string' ? className : '';
13
+ }
14
+
7
15
  // Jest resolves `../content-panel` to the native variant (`index.tsx`), which
8
16
  // does not read the theme — so these render without a BloomThemeProvider. The
9
17
  // nesting guard lives in the shared `nesting-context` module exercised by both
@@ -50,3 +58,69 @@ describe('ContentPanel nesting guard', () => {
50
58
  consoleError.mockRestore();
51
59
  });
52
60
  });
61
+
62
+ // The responsive framing breakpoint is configurable via `framedFrom` and is
63
+ // resolved entirely with literal NativeWind classes. These assert the native
64
+ // surface picks up the pre-shipped literal bundle for each breakpoint value.
65
+ describe('ContentPanel framedFrom breakpoint', () => {
66
+ it('defaults to the md (768) named-screen bundle when omitted', () => {
67
+ const className = surfaceClassName(
68
+ render(
69
+ <ContentPanel>
70
+ <Text>content</Text>
71
+ </ContentPanel>,
72
+ ),
73
+ );
74
+ expect(className).toContain('md:rounded-radius-28');
75
+ expect(className).toContain('md:border');
76
+ expect(className).not.toContain('min-[500px]:');
77
+ });
78
+
79
+ it('uses the arbitrary min-[500px] bundle for framedFrom={500}', () => {
80
+ const className = surfaceClassName(
81
+ render(
82
+ <ContentPanel framedFrom={500}>
83
+ <Text>content</Text>
84
+ </ContentPanel>,
85
+ ),
86
+ );
87
+ expect(className).toContain('min-[500px]:rounded-radius-28');
88
+ expect(className).toContain('min-[500px]:border');
89
+ expect(className).not.toContain('md:');
90
+ });
91
+
92
+ it('uses named sm (640) and lg (1024) bundles', () => {
93
+ expect(
94
+ surfaceClassName(
95
+ render(
96
+ <ContentPanel framedFrom={640}>
97
+ <Text>content</Text>
98
+ </ContentPanel>,
99
+ ),
100
+ ),
101
+ ).toContain('sm:rounded-radius-28');
102
+ expect(
103
+ surfaceClassName(
104
+ render(
105
+ <ContentPanel framedFrom={1024}>
106
+ <Text>content</Text>
107
+ </ContentPanel>,
108
+ ),
109
+ ),
110
+ ).toContain('lg:rounded-radius-28');
111
+ });
112
+
113
+ it('ignores framedFrom when framed is explicitly true (always framed)', () => {
114
+ const className = surfaceClassName(
115
+ render(
116
+ <ContentPanel framed framedFrom={500}>
117
+ <Text>content</Text>
118
+ </ContentPanel>,
119
+ ),
120
+ );
121
+ // Always-framed uses unconditional (non-breakpoint) rounding + border.
122
+ expect(className).toContain('rounded-radius-28');
123
+ expect(className).not.toContain('min-[500px]:');
124
+ expect(className).not.toContain('md:');
125
+ });
126
+ });
@@ -11,11 +11,12 @@
11
11
  * scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
12
12
  * surface wrapping its content. The `framed` prop still drives whether that
13
13
  * surface is rounded + bordered, tri-state and resolved with pure NativeWind
14
- * (`md:` evaluates against the window width on native too — no JS breakpoint
15
- * hook needed):
14
+ * (the breakpoint's `min-*:` variant evaluates against the window width on
15
+ * native too — no JS breakpoint hook needed):
16
16
  *
17
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
18
- * bordered at `md`+ (`md:rounded-radius-28 md:border md:border-border`).
17
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
18
+ * breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
19
+ * (e.g. `md:rounded-radius-28 md:border md:border-border`).
19
20
  * - `false` → never framed (plain full-bleed at every size).
20
21
  * - `true` → always rounded + bordered.
21
22
  *
@@ -47,17 +48,35 @@ export const PANEL_TOP_INSET = 8;
47
48
  /** Bottom sticky inset (px) of the framed panel chrome. */
48
49
  export const PANEL_BOTTOM_INSET = 8;
49
50
 
51
+ /**
52
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
53
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
54
+ * Tailwind class bundle — `768` (default) uses the named `md:` screen
55
+ * (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
56
+ * `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
57
+ * 500px). The breakpoint tokens are never built at runtime, so a consumer's
58
+ * Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
59
+ */
60
+ export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
61
+
50
62
  export interface ContentPanelProps {
51
63
  children: React.ReactNode;
52
64
  /**
53
65
  * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
54
66
  * breakpoint hook needed:
55
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, rounded +
56
- * bordered at `md`+.
67
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
68
+ * breakpoint, rounded + bordered at/above it.
57
69
  * - `false` → never framed (plain full-bleed at every size).
58
70
  * - `true` → always rounded + bordered.
59
71
  */
60
72
  framed?: boolean;
73
+ /**
74
+ * Viewport width at which the RESPONSIVE panel switches from full-bleed to
75
+ * framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
76
+ * when `framed` is `true` (always framed) or `false` (never framed). Defaults
77
+ * to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
78
+ */
79
+ framedFrom?: ContentPanelFramedBreakpoint;
61
80
  /** Override the surface background utility (defaults to `bg-card`). */
62
81
  surfaceClassName?: string;
63
82
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -76,9 +95,24 @@ export interface ContentPanelProps {
76
95
  maskColor?: string;
77
96
  }
78
97
 
98
+ /**
99
+ * Per-breakpoint literal Tailwind surface class bundle for the NATIVE responsive
100
+ * mode. Each value is a WHOLE literal string (never assembled from parts at
101
+ * runtime) so the Tailwind content-scan over `src/**`/`lib/**` picks up every
102
+ * `min-*`/`sm:`/`md:`/`lg:` token verbatim. Selecting a bundle by `framedFrom`
103
+ * value is the only runtime decision.
104
+ */
105
+ const RESPONSIVE_SURFACE: Record<ContentPanelFramedBreakpoint, string> = {
106
+ 500: 'min-[500px]:overflow-hidden min-[500px]:rounded-radius-28 min-[500px]:border min-[500px]:border-border',
107
+ 640: 'sm:overflow-hidden sm:rounded-radius-28 sm:border sm:border-border',
108
+ 768: 'md:overflow-hidden md:rounded-radius-28 md:border md:border-border',
109
+ 1024: 'lg:overflow-hidden lg:rounded-radius-28 lg:border lg:border-border',
110
+ };
111
+
79
112
  const ContentPanelComponent: React.FC<ContentPanelProps> = ({
80
113
  children,
81
114
  framed,
115
+ framedFrom = 768,
82
116
  surfaceClassName,
83
117
  surfaceStyle,
84
118
  contentClassName,
@@ -87,13 +121,15 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
87
121
  // Dev-only invariant — a ContentPanel must never be nested inside another.
88
122
  useContentPanelNestingGuard();
89
123
 
90
- // Tri-state: `undefined` → responsive (md:-gated), `true` → always framed,
91
- // `false` → never framed (plain full-bleed). Whole literal class strings are
92
- // selected per mode so the Tailwind content-scan over `src/**` picks up every
93
- // `md:` / `rounded-radius-28` token verbatim (no dynamic concatenation).
124
+ // Tri-state: `undefined` → responsive (breakpoint-gated), `true` → always
125
+ // framed, `false` → never framed (plain full-bleed). Whole literal class
126
+ // strings are selected per mode so the Tailwind content-scan over `src/**`
127
+ // picks up every `min-*`/`sm:`/`md:`/`lg:` / `rounded-radius-28` token verbatim
128
+ // (the breakpoint bundle lives whole in `RESPONSIVE_SURFACE`; `framed ===
129
+ // true`/`false` ignore `framedFrom` for fixed always/never framing).
94
130
  const surfaceBase =
95
131
  framed === undefined
96
- ? 'flex-1 md:overflow-hidden md:rounded-radius-28 md:border md:border-border'
132
+ ? `flex-1 ${RESPONSIVE_SURFACE[framedFrom]}`
97
133
  : framed
98
134
  ? 'flex-1 overflow-hidden rounded-radius-28 border border-border'
99
135
  : 'flex-1';
@@ -26,18 +26,22 @@
26
26
  * Framing is tri-state via the `framed` prop:
27
27
  *
28
28
  * - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
29
- * below `md`, framed at `md`+. The rounding is `md:`-gated and both overlays
30
- * are always rendered but carry `max-md:hidden` (`display:none` below `md`),
31
- * so the breakpoint is decided in CSS no consumer JS breakpoint hook.
29
+ * below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
30
+ * configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
31
+ * rounding is gated by that breakpoint's `min-*:` variant and both overlays
32
+ * are always rendered but carry the matching `max-*:hidden` literal
33
+ * (`display:none` below the breakpoint), so the breakpoint is decided entirely
34
+ * in CSS — no consumer JS breakpoint hook.
32
35
  * - `false` → NEVER framed (full-bleed at every size): a single flat surface,
33
36
  * no rounding, no overlays.
34
37
  * - `true` → ALWAYS framed (rounded + overlays at every size).
35
38
  *
36
- * The overlays are gated by VISIBILITY (a `max-md:hidden` literal) rather than
37
- * by conditional mounting for the responsive case, so crossing the breakpoint
38
- * never remounts them. The content wrapper likewise keeps a stable `key` so
39
- * `{children}` reconciles in place across the breakpoint instead of remounting
40
- * (which would reset feed scroll / the virtualizer + refetch).
39
+ * The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
40
+ * breakpoint) rather than by conditional mounting for the responsive case, so
41
+ * crossing the breakpoint never remounts them. The content wrapper likewise
42
+ * keeps a stable `key` so `{children}` reconciles in place across the breakpoint
43
+ * instead of remounting (which would reset feed scroll / the virtualizer +
44
+ * refetch).
41
45
  *
42
46
  * Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
43
47
  * select this file via the `"browser"` export condition in `package.json`.
@@ -63,17 +67,37 @@ export const PANEL_TOP_INSET = 8;
63
67
  /** Bottom sticky inset (px) of the framed panel chrome. */
64
68
  export const PANEL_BOTTOM_INSET = 8;
65
69
 
70
+ /**
71
+ * Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
72
+ * switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
73
+ * Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
74
+ * (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
75
+ * `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
76
+ * variants (there is no named screen at 500px). The breakpoint tokens are never
77
+ * built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
78
+ * them verbatim.
79
+ */
80
+ export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
81
+
66
82
  export interface ContentPanelProps {
67
83
  children: React.ReactNode;
68
84
  /**
69
85
  * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
70
86
  * breakpoint hook needed:
71
- * - `undefined` (DEFAULT) → responsive: full-bleed below `md`, framed at
72
- * `md`+ (rounded corners + sticky bleed-mask/border overlays).
87
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
88
+ * breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
89
+ * overlays).
73
90
  * - `false` → never framed (full-bleed at every size).
74
91
  * - `true` → always framed (rounded + overlays at every size).
75
92
  */
76
93
  framed?: boolean;
94
+ /**
95
+ * Viewport width at which the RESPONSIVE panel switches from full-bleed to
96
+ * framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
97
+ * when `framed` is `true` (always framed) or `false` (never framed). Defaults
98
+ * to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
99
+ */
100
+ framedFrom?: ContentPanelFramedBreakpoint;
77
101
  /** Override the surface background utility (defaults to `bg-card`). */
78
102
  surfaceClassName?: string;
79
103
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -93,9 +117,50 @@ export interface ContentPanelProps {
93
117
  maskColor?: string;
94
118
  }
95
119
 
120
+ /**
121
+ * Per-breakpoint literal Tailwind variant tokens for the WEB responsive mode.
122
+ * Each field is a WHOLE literal class string (never assembled from parts at
123
+ * runtime) so the consumer's content-scan over `lib/**` picks up every
124
+ * arbitrary/`min-*`/`max-*`/`web:` token verbatim. Selecting a bundle by
125
+ * `framedFrom` value is the only runtime decision:
126
+ * - `surface` → framing rounding on the surface at/above the breakpoint.
127
+ * - `content` → the same rounding + a web-only horizontal clip on the
128
+ * inner content wrapper (`web:<bp>:overflow-x-clip`, platform-first-then-
129
+ * breakpoint — matching the consumer convention `web:sm:flex` in `@mercaria/ui`).
130
+ * - `overlayHidden` → `display:none` for the sticky overlays BELOW the breakpoint.
131
+ * `min-[500px]:` (≥500) and `max-[500px]:` (<500) are exactly complementary
132
+ * (meet at 500, no overlap/gap), mirroring the named `md:`/`max-md:` pair.
133
+ */
134
+ const RESPONSIVE_WEB: Record<
135
+ ContentPanelFramedBreakpoint,
136
+ { surface: string; content: string; overlayHidden: string }
137
+ > = {
138
+ 500: {
139
+ surface: 'min-[500px]:rounded-radius-28',
140
+ content: 'min-[500px]:rounded-radius-28 web:min-[500px]:overflow-x-clip',
141
+ overlayHidden: 'max-[500px]:hidden',
142
+ },
143
+ 640: {
144
+ surface: 'sm:rounded-radius-28',
145
+ content: 'sm:rounded-radius-28 web:sm:overflow-x-clip',
146
+ overlayHidden: 'max-sm:hidden',
147
+ },
148
+ 768: {
149
+ surface: 'md:rounded-radius-28',
150
+ content: 'md:rounded-radius-28 web:md:overflow-x-clip',
151
+ overlayHidden: 'max-md:hidden',
152
+ },
153
+ 1024: {
154
+ surface: 'lg:rounded-radius-28',
155
+ content: 'lg:rounded-radius-28 web:lg:overflow-x-clip',
156
+ overlayHidden: 'max-lg:hidden',
157
+ },
158
+ };
159
+
96
160
  const ContentPanelComponent: React.FC<ContentPanelProps> = ({
97
161
  children,
98
162
  framed,
163
+ framedFrom = 768,
99
164
  surfaceClassName,
100
165
  surfaceStyle,
101
166
  contentClassName,
@@ -112,18 +177,21 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
112
177
  // `false` → never framed (full-bleed).
113
178
  const responsive = framed === undefined;
114
179
  const showOverlays = framed !== false;
180
+ // Responsive mode selects a pre-shipped literal breakpoint bundle by value;
181
+ // `framed === true`/`false` ignore `framedFrom` (fixed always/never framing).
182
+ const bp = RESPONSIVE_WEB[framedFrom];
115
183
 
116
184
  // Whole literal class strings selected per mode (the Tailwind content-scan
117
- // over `lib/**` requires each arbitrary/`md:`/`web:` token stay literal).
118
- // `web:md:overflow-x-clip` is platform-first-then-breakpoint, matching the
119
- // consumer convention (`web:sm:flex` in `@mercaria/ui`).
185
+ // over `lib/**` requires each arbitrary/`min-*`/`max-*`/`web:` token stay
186
+ // literal — the breakpoint tokens live whole in `RESPONSIVE_WEB` above and are
187
+ // only spliced in, never assembled from parts).
120
188
  const surfaceBase = responsive
121
- ? 'flex-1 md:rounded-radius-28'
189
+ ? `flex-1 ${bp.surface}`
122
190
  : framed
123
191
  ? 'flex-1 rounded-radius-28'
124
192
  : 'flex-1';
125
193
  const contentBase = responsive
126
- ? 'flex-1 md:rounded-radius-28 web:md:overflow-x-clip'
194
+ ? `flex-1 ${bp.content}`
127
195
  : framed
128
196
  ? 'flex-1 rounded-radius-28 web:overflow-x-clip'
129
197
  : 'flex-1';
@@ -145,7 +213,7 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
145
213
  pointerEvents="none"
146
214
  {...({
147
215
  className: responsive
148
- ? 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 max-md:hidden web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]'
216
+ ? `web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]`
149
217
  : 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
150
218
  } as Record<string, string>)}
151
219
  style={{ boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}` }}
@@ -159,7 +227,7 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
159
227
  pointerEvents="none"
160
228
  {...({
161
229
  className: responsive
162
- ? 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border max-md:hidden web:[margin-bottom:calc(-100dvh+16px)]'
230
+ ? `web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)]`
163
231
  : 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]',
164
232
  } as Record<string, string>)}
165
233
  />