@nok-integration/storefront-react 0.19.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -5997,16 +5997,38 @@ declare const SupportTicketRequest: z.ZodObject<{
5997
5997
  * a page showing another customer's data.
5998
5998
  */
5999
5999
  image_url: z.ZodOptional<z.ZodString>;
6000
+ /**
6001
+ * The fan this ticket belongs to — **only when the caller is a DAZN service holding a
6002
+ * support API key**, and refused outright when the caller holds a shop session.
6003
+ *
6004
+ * Optional here because one field cannot be required of one caller and forbidden to
6005
+ * another in a single schema. The engine decides which rule applies from how the
6006
+ * request authenticated, and rejects the wrong combination either way: absent under a
6007
+ * key is a 400, present under a session is a 400.
6008
+ *
6009
+ * **This is not a relaxation of the rule it looks like a relaxation of.** The rule is
6010
+ * that an *untrusted* caller may not name the fan: a browser that could would let
6011
+ * anyone file a ticket as anyone, and CS would answer it in good faith with another
6012
+ * customer's account detail. A backend authenticated by a shared secret is not that
6013
+ * caller. It is authoritative for its own users in exactly the way order intake
6014
+ * already is when DAZN tells us whose order they have taken.
6015
+ *
6016
+ * Added for DAZN's Contact Support form, which lives outside the shop and therefore
6017
+ * has no shop session to read an identity from (21 Aug 2026).
6018
+ */
6019
+ dazn_user_id: z.ZodOptional<z.ZodString>;
6000
6020
  }, "strict", z.ZodTypeAny, {
6001
6021
  email: string;
6002
6022
  description: string;
6003
6023
  category: string;
6004
6024
  image_url?: string | undefined;
6025
+ dazn_user_id?: string | undefined;
6005
6026
  }, {
6006
6027
  email: string;
6007
6028
  description: string;
6008
6029
  category: string;
6009
6030
  image_url?: string | undefined;
6031
+ dazn_user_id?: string | undefined;
6010
6032
  }>;
6011
6033
 
6012
6034
  declare type SupportTicketRequest = z.infer<typeof SupportTicketRequest>;
package/dist/index.mjs CHANGED
@@ -5141,7 +5141,27 @@ function requireSupport() {
5141
5141
  * CS agent's browser fetch whatever a stranger typed, from inside DAZN's network, on
5142
5142
  * a page showing another customer's data.
5143
5143
  */
5144
- image_url: zod_1.z.string().url().max(2048).optional()
5144
+ image_url: zod_1.z.string().url().max(2048).optional(),
5145
+ /**
5146
+ * The fan this ticket belongs to — **only when the caller is a DAZN service holding a
5147
+ * support API key**, and refused outright when the caller holds a shop session.
5148
+ *
5149
+ * Optional here because one field cannot be required of one caller and forbidden to
5150
+ * another in a single schema. The engine decides which rule applies from how the
5151
+ * request authenticated, and rejects the wrong combination either way: absent under a
5152
+ * key is a 400, present under a session is a 400.
5153
+ *
5154
+ * **This is not a relaxation of the rule it looks like a relaxation of.** The rule is
5155
+ * that an *untrusted* caller may not name the fan: a browser that could would let
5156
+ * anyone file a ticket as anyone, and CS would answer it in good faith with another
5157
+ * customer's account detail. A backend authenticated by a shared secret is not that
5158
+ * caller. It is authoritative for its own users in exactly the way order intake
5159
+ * already is when DAZN tells us whose order they have taken.
5160
+ *
5161
+ * Added for DAZN's Contact Support form, which lives outside the shop and therefore
5162
+ * has no shop session to read an identity from (21 Aug 2026).
5163
+ */
5164
+ dazn_user_id: zod_1.z.string().trim().min(1).max(128).optional()
5145
5165
  }).strict();
5146
5166
  exports.SupportTicket = zod_1.z.object({
5147
5167
  reference: zod_1.z.string().min(1),
@@ -8904,15 +8924,15 @@ function CartSheetProvider({ children }) {
8904
8924
  isOpen && /* @__PURE__ */ jsx(BottomSheet, { open: true, onClose: close2, title: t("Cart"), children: /* @__PURE__ */ jsx(NoScreenTitle, { children: /* @__PURE__ */ jsx(CartView, { onContinueShopping: close2 }) }) })
8905
8925
  ] });
8906
8926
  }
8907
- const gallery = "_gallery_10mch_1";
8908
- const track$2 = "_track_10mch_8";
8909
- const slide$1 = "_slide_10mch_32";
8910
- const image$4 = "_image_10mch_39";
8911
- const placeholder$1 = "_placeholder_10mch_43";
8912
- const share = "_share_10mch_50";
8913
- const dots$2 = "_dots_10mch_67";
8914
- const dot$2 = "_dot_10mch_67";
8915
- const dotActive$2 = "_dotActive_10mch_91";
8927
+ const gallery = "_gallery_9e68b_1";
8928
+ const track$2 = "_track_9e68b_8";
8929
+ const slide$1 = "_slide_9e68b_32";
8930
+ const image$4 = "_image_9e68b_39";
8931
+ const placeholder$1 = "_placeholder_9e68b_43";
8932
+ const share = "_share_9e68b_50";
8933
+ const dots$2 = "_dots_9e68b_83";
8934
+ const dot$2 = "_dot_9e68b_83";
8935
+ const dotActive$2 = "_dotActive_9e68b_134";
8916
8936
  const styles$i = {
8917
8937
  gallery,
8918
8938
  track: track$2,
@@ -9136,16 +9156,16 @@ function SizeGuideSheet({ guide, onClose }) {
9136
9156
  }
9137
9157
  ) });
9138
9158
  }
9139
- const root$2 = "_root_9zj90_1";
9140
- const header$3 = "_header_9zj90_7";
9141
- const title$9 = "_title_9zj90_12";
9142
- const chips = "_chips_9zj90_20";
9143
- const chip = "_chip_9zj90_20";
9144
- const selected$2 = "_selected_9zj90_44";
9145
- const disabled$1 = "_disabled_9zj90_56";
9146
- const error = "_error_9zj90_64";
9147
- const sizeGuide = "_sizeGuide_9zj90_71";
9148
- const chevron$2 = "_chevron_9zj90_85";
9159
+ const root$2 = "_root_6sbl9_1";
9160
+ const header$3 = "_header_6sbl9_7";
9161
+ const title$9 = "_title_6sbl9_12";
9162
+ const chips = "_chips_6sbl9_20";
9163
+ const chip = "_chip_6sbl9_20";
9164
+ const selected$2 = "_selected_6sbl9_44";
9165
+ const disabled$1 = "_disabled_6sbl9_56";
9166
+ const error = "_error_6sbl9_64";
9167
+ const sizeGuide = "_sizeGuide_6sbl9_71";
9168
+ const chevron$2 = "_chevron_6sbl9_87";
9149
9169
  const styles$g = {
9150
9170
  root: root$2,
9151
9171
  header: header$3,
@@ -9345,13 +9365,13 @@ function VariantSelector({
9345
9365
  }) })
9346
9366
  ] });
9347
9367
  }
9348
- const root = "_root_t4qba_2";
9349
- const group = "_group_t4qba_8";
9350
- const title$7 = "_title_t4qba_14";
9351
- const body$1 = "_body_t4qba_22";
9352
- const paragraph = "_paragraph_t4qba_26";
9353
- const clamped = "_clamped_t4qba_46";
9354
- const toggle = "_toggle_t4qba_51";
9368
+ const root = "_root_9cj0g_2";
9369
+ const group = "_group_9cj0g_8";
9370
+ const title$7 = "_title_9cj0g_14";
9371
+ const body$1 = "_body_9cj0g_22";
9372
+ const paragraph = "_paragraph_9cj0g_26";
9373
+ const clamped = "_clamped_9cj0g_46";
9374
+ const toggle = "_toggle_9cj0g_51";
9355
9375
  const styles$d = {
9356
9376
  root,
9357
9377
  group,
@@ -5141,7 +5141,27 @@ function requireSupport() {
5141
5141
  * CS agent's browser fetch whatever a stranger typed, from inside DAZN's network, on
5142
5142
  * a page showing another customer's data.
5143
5143
  */
5144
- image_url: zod_1.z.string().url().max(2048).optional()
5144
+ image_url: zod_1.z.string().url().max(2048).optional(),
5145
+ /**
5146
+ * The fan this ticket belongs to — **only when the caller is a DAZN service holding a
5147
+ * support API key**, and refused outright when the caller holds a shop session.
5148
+ *
5149
+ * Optional here because one field cannot be required of one caller and forbidden to
5150
+ * another in a single schema. The engine decides which rule applies from how the
5151
+ * request authenticated, and rejects the wrong combination either way: absent under a
5152
+ * key is a 400, present under a session is a 400.
5153
+ *
5154
+ * **This is not a relaxation of the rule it looks like a relaxation of.** The rule is
5155
+ * that an *untrusted* caller may not name the fan: a browser that could would let
5156
+ * anyone file a ticket as anyone, and CS would answer it in good faith with another
5157
+ * customer's account detail. A backend authenticated by a shared secret is not that
5158
+ * caller. It is authoritative for its own users in exactly the way order intake
5159
+ * already is when DAZN tells us whose order they have taken.
5160
+ *
5161
+ * Added for DAZN's Contact Support form, which lives outside the shop and therefore
5162
+ * has no shop session to read an identity from (21 Aug 2026).
5163
+ */
5164
+ dazn_user_id: zod_1.z.string().trim().min(1).max(128).optional()
5145
5165
  }).strict();
5146
5166
  exports.SupportTicket = zod_1.z.object({
5147
5167
  reference: zod_1.z.string().min(1),
@@ -8904,15 +8924,15 @@ function CartSheetProvider({ children }) {
8904
8924
  isOpen && /* @__PURE__ */ jsx(BottomSheet, { open: true, onClose: close2, title: t("Cart"), children: /* @__PURE__ */ jsx(NoScreenTitle, { children: /* @__PURE__ */ jsx(CartView, { onContinueShopping: close2 }) }) })
8905
8925
  ] });
8906
8926
  }
8907
- const gallery = "_gallery_10mch_1";
8908
- const track$2 = "_track_10mch_8";
8909
- const slide$1 = "_slide_10mch_32";
8910
- const image$4 = "_image_10mch_39";
8911
- const placeholder$1 = "_placeholder_10mch_43";
8912
- const share = "_share_10mch_50";
8913
- const dots$2 = "_dots_10mch_67";
8914
- const dot$2 = "_dot_10mch_67";
8915
- const dotActive$2 = "_dotActive_10mch_91";
8927
+ const gallery = "_gallery_9e68b_1";
8928
+ const track$2 = "_track_9e68b_8";
8929
+ const slide$1 = "_slide_9e68b_32";
8930
+ const image$4 = "_image_9e68b_39";
8931
+ const placeholder$1 = "_placeholder_9e68b_43";
8932
+ const share = "_share_9e68b_50";
8933
+ const dots$2 = "_dots_9e68b_83";
8934
+ const dot$2 = "_dot_9e68b_83";
8935
+ const dotActive$2 = "_dotActive_9e68b_134";
8916
8936
  const styles$i = {
8917
8937
  gallery,
8918
8938
  track: track$2,
@@ -9136,16 +9156,16 @@ function SizeGuideSheet({ guide, onClose }) {
9136
9156
  }
9137
9157
  ) });
9138
9158
  }
9139
- const root$2 = "_root_9zj90_1";
9140
- const header$3 = "_header_9zj90_7";
9141
- const title$9 = "_title_9zj90_12";
9142
- const chips = "_chips_9zj90_20";
9143
- const chip = "_chip_9zj90_20";
9144
- const selected$2 = "_selected_9zj90_44";
9145
- const disabled$1 = "_disabled_9zj90_56";
9146
- const error = "_error_9zj90_64";
9147
- const sizeGuide = "_sizeGuide_9zj90_71";
9148
- const chevron$2 = "_chevron_9zj90_85";
9159
+ const root$2 = "_root_6sbl9_1";
9160
+ const header$3 = "_header_6sbl9_7";
9161
+ const title$9 = "_title_6sbl9_12";
9162
+ const chips = "_chips_6sbl9_20";
9163
+ const chip = "_chip_6sbl9_20";
9164
+ const selected$2 = "_selected_6sbl9_44";
9165
+ const disabled$1 = "_disabled_6sbl9_56";
9166
+ const error = "_error_6sbl9_64";
9167
+ const sizeGuide = "_sizeGuide_6sbl9_71";
9168
+ const chevron$2 = "_chevron_6sbl9_87";
9149
9169
  const styles$g = {
9150
9170
  root: root$2,
9151
9171
  header: header$3,
@@ -9345,13 +9365,13 @@ function VariantSelector({
9345
9365
  }) })
9346
9366
  ] });
9347
9367
  }
9348
- const root = "_root_t4qba_2";
9349
- const group = "_group_t4qba_8";
9350
- const title$7 = "_title_t4qba_14";
9351
- const body$1 = "_body_t4qba_22";
9352
- const paragraph = "_paragraph_t4qba_26";
9353
- const clamped = "_clamped_t4qba_46";
9354
- const toggle = "_toggle_t4qba_51";
9368
+ const root = "_root_9cj0g_2";
9369
+ const group = "_group_9cj0g_8";
9370
+ const title$7 = "_title_9cj0g_14";
9371
+ const body$1 = "_body_9cj0g_22";
9372
+ const paragraph = "_paragraph_9cj0g_26";
9373
+ const clamped = "_clamped_9cj0g_46";
9374
+ const toggle = "_toggle_9cj0g_51";
9355
9375
  const styles$d = {
9356
9376
  root,
9357
9377
  group,
@@ -3647,14 +3647,14 @@
3647
3647
  gap: var(--space-20);
3648
3648
  }
3649
3649
 
3650
- ._gallery_10mch_1 {
3650
+ ._gallery_9e68b_1 {
3651
3651
  position: relative;
3652
3652
  display: flex;
3653
3653
  flex-direction: column;
3654
3654
  gap: var(--space-12);
3655
3655
  }
3656
3656
 
3657
- ._track_10mch_8 {
3657
+ ._track_9e68b_8 {
3658
3658
  display: flex;
3659
3659
  overflow-x: auto;
3660
3660
  scroll-snap-type: x mandatory;
@@ -3674,30 +3674,46 @@
3674
3674
  scrollbar-width: none;
3675
3675
  }
3676
3676
 
3677
- ._track_10mch_8::-webkit-scrollbar {
3677
+ ._track_9e68b_8::-webkit-scrollbar {
3678
3678
  display: none;
3679
3679
  }
3680
3680
 
3681
- ._slide_10mch_32 {
3681
+ ._slide_9e68b_32 {
3682
3682
  position: relative;
3683
3683
  flex: 0 0 100%;
3684
3684
  scroll-snap-align: center;
3685
3685
  aspect-ratio: 375 / 475;
3686
3686
  }
3687
3687
 
3688
- ._image_10mch_39 {
3688
+ ._image_9e68b_39 {
3689
3689
  object-fit: cover;
3690
3690
  }
3691
3691
 
3692
- ._placeholder_10mch_43 {
3692
+ ._placeholder_9e68b_43 {
3693
3693
  width: 100%;
3694
3694
  aspect-ratio: 375 / 475;
3695
3695
  background: var(--color-surface-soft);
3696
3696
  }
3697
3697
 
3698
3698
  /* Share button — top-right chrome over the image (mobile/button: 32px, soft, radius-8). */
3699
- ._share_10mch_50 {
3699
+ ._share_9e68b_50 {
3700
3700
  position: absolute;
3701
+ /*
3702
+ * **Without this the button is drawn and cannot be tapped.**
3703
+ *
3704
+ * `.share` is the gallery's first child and the image track follows it. Both the button
3705
+ * and the `fill` images inside the track are absolutely positioned and nothing here
3706
+ * declared a `z-index`, so painting order fell to DOM order and every image won. A tap
3707
+ * anywhere on the icon landed on the photograph — the control looked completely normal
3708
+ * and did nothing, which is the failure mode this repo has been bitten by before.
3709
+ *
3710
+ * The pagination dots were never affected: they come *after* the track, so they were
3711
+ * already on top by accident. That is why this survived review.
3712
+ *
3713
+ * Found by hit-testing `document.elementFromPoint` at the button's own centre, not by
3714
+ * reading the stylesheet — from the CSS it looks correct.
3715
+ */
3716
+ z-index: 1;
3701
3717
  top: var(--space-16);
3702
3718
  right: var(--space-16);
3703
3719
  display: inline-flex;
@@ -3713,13 +3729,25 @@
3713
3729
  cursor: pointer;
3714
3730
  }
3715
3731
 
3716
- ._dots_10mch_67 {
3732
+ ._dots_9e68b_83 {
3717
3733
  display: flex;
3718
3734
  justify-content: center;
3719
3735
  gap: var(--space-6);
3720
3736
  }
3721
3737
 
3722
- ._dot_10mch_67 {
3738
+ /*
3739
+ * The icon is drawn at 32px because that is the box the design gives it; the TAP is 44px.
3740
+ * Same trick as the header's controls — a negative-inset pseudo-element, so the hit area
3741
+ * grows and not one pixel moves. Measured 32px of clear space above it, so symmetric
3742
+ * expansion cannot reach the control overhead.
3743
+ */
3744
+ ._share_9e68b_50::after {
3745
+ content: '';
3746
+ position: absolute;
3747
+ inset: -6px;
3748
+ }
3749
+
3750
+ ._dot_9e68b_83 {
3723
3751
  width: 8px;
3724
3752
  height: 8px;
3725
3753
  padding: 0;
@@ -3731,13 +3759,28 @@
3731
3759
  position: relative;
3732
3760
  }
3733
3761
 
3734
- ._dot_10mch_67::after {
3762
+ /*
3763
+ * The hit area is tall, and only as wide as the dot's own column.
3764
+ *
3765
+ * It used to be `inset: -18px` on all four sides, which reads as "44px target" and behaved
3766
+ * as the opposite. The dots are 8px with a 6px gap — a 14px pitch — so an 18px horizontal
3767
+ * bleed put every dot's hit area completely over its neighbour's, and the later sibling
3768
+ * paints on top. **Only the last dot was tappable**; aiming at the first selected the
3769
+ * second. Measured by walking `elementFromPoint` outward from each dot's centre.
3770
+ *
3771
+ * 3px is half the gap, so the columns tile exactly and never overlap. That makes each dot
3772
+ * 14px wide, which is under the 44px guideline and is the widest the design's own spacing
3773
+ * allows without one dot stealing another's taps — a reliable 14x44 beats a nominal 44x44
3774
+ * that belongs to the wrong control. Swiping the track remains the primary affordance, and
3775
+ * it is unaffected.
3776
+ */
3777
+ ._dot_9e68b_83::after {
3735
3778
  content: '';
3736
3779
  position: absolute;
3737
- inset: -18px;
3780
+ inset: -18px -3px;
3738
3781
  }
3739
3782
 
3740
- ._dotActive_10mch_91 {
3783
+ ._dotActive_9e68b_134 {
3741
3784
  background: var(--color-surface-max);
3742
3785
  }
3743
3786
  /*
@@ -4115,18 +4158,18 @@
4115
4158
  animation: none;
4116
4159
  }
4117
4160
  }
4118
- ._root_9zj90_1 {
4161
+ ._root_6sbl9_1 {
4119
4162
  display: flex;
4120
4163
  flex-direction: column;
4121
4164
  gap: var(--space-12);
4122
4165
  }
4123
4166
 
4124
- ._header_9zj90_7 {
4167
+ ._header_6sbl9_7 {
4125
4168
  display: flex;
4126
4169
  align-items: center;
4127
4170
  }
4128
4171
 
4129
- ._title_9zj90_12 {
4172
+ ._title_6sbl9_12 {
4130
4173
  margin: 0;
4131
4174
  font-size: var(--type-trimmed-md-size);
4132
4175
  line-height: var(--type-trimmed-md-line);
@@ -4134,14 +4177,14 @@
4134
4177
  color: var(--color-text-default);
4135
4178
  }
4136
4179
 
4137
- ._chips_9zj90_20 {
4180
+ ._chips_6sbl9_20 {
4138
4181
  display: flex;
4139
4182
  flex-wrap: wrap;
4140
4183
  gap: var(--space-8);
4141
4184
  }
4142
4185
 
4143
4186
  /* 48px square chips per the mobile/pdp/size component (radius-xs, not radius-sm). */
4144
- ._chip_9zj90_20 {
4187
+ ._chip_6sbl9_20 {
4145
4188
  display: inline-flex;
4146
4189
  align-items: center;
4147
4190
  justify-content: center;
@@ -4158,7 +4201,7 @@
4158
4201
  cursor: pointer;
4159
4202
  }
4160
4203
 
4161
- ._selected_9zj90_44 {
4204
+ ._selected_6sbl9_44 {
4162
4205
  border-color: var(--color-surface-max);
4163
4206
  background: var(--color-surface-max);
4164
4207
  color: var(--color-text-strong);
@@ -4170,7 +4213,7 @@
4170
4213
  * range reads complete"), so a fan can see the range runs to XXL and that their size is
4171
4214
  * the one that has gone, rather than seeing a range with a hole in it.
4172
4215
  */
4173
- ._disabled_9zj90_56 {
4216
+ ._disabled_6sbl9_56 {
4174
4217
  cursor: not-allowed;
4175
4218
  border-style: dashed;
4176
4219
  color: var(--color-text-disabled);
@@ -4178,14 +4221,16 @@
4178
4221
 
4179
4222
  /* Figma: regular weight, critical token. Bold read as a second heading next to the
4180
4223
  16px "Size" label above it. */
4181
- ._error_9zj90_64 {
4224
+ ._error_6sbl9_64 {
4182
4225
  margin: 0;
4183
4226
  font-size: var(--type-trimmed-sm-size);
4184
4227
  font-weight: var(--font-weight-regular);
4185
4228
  color: var(--color-state-surface-critical-default);
4186
4229
  }
4187
4230
 
4188
- ._sizeGuide_9zj90_71 {
4231
+ ._sizeGuide_6sbl9_71 {
4232
+ /* Anchors the hit-area pseudo-element below. */
4233
+ position: relative;
4189
4234
  display: inline-flex;
4190
4235
  align-items: center;
4191
4236
  gap: var(--space-2);
@@ -4199,9 +4244,23 @@
4199
4244
  cursor: pointer;
4200
4245
  }
4201
4246
 
4202
- ._chevron_9zj90_85 {
4247
+ ._chevron_6sbl9_87 {
4203
4248
  display: block;
4204
4249
  }
4250
+
4251
+ /*
4252
+ * An 18px-tall text button is a 44px tap target.
4253
+ *
4254
+ * **Asymmetric on purpose.** There are only 11px of clear space between this row and the
4255
+ * size chips above it, so a symmetric -13px would put this control's hit area *over* the
4256
+ * chips — and a fan aiming at XL would open the size guide instead. It grows 8px up (3px
4257
+ * of the gap left clear) and 18px down, into empty space: 8 + 18 + 18 = 44.
4258
+ */
4259
+ ._sizeGuide_6sbl9_71::after {
4260
+ content: '';
4261
+ position: absolute;
4262
+ inset: -8px -8px -18px;
4263
+ }
4205
4264
  ._root_1h4ny_1 {
4206
4265
  display: flex;
4207
4266
  flex-direction: column;
@@ -4362,19 +4421,19 @@
4362
4421
  background: var(--color-surface-soft);
4363
4422
  }
4364
4423
  /* Figma mobile/pdp/details: heading+body group, then more/Less 4px under it. */
4365
- ._root_t4qba_2 {
4424
+ ._root_9cj0g_2 {
4366
4425
  display: flex;
4367
4426
  flex-direction: column;
4368
4427
  gap: var(--space-4);
4369
4428
  }
4370
4429
 
4371
- ._group_t4qba_8 {
4430
+ ._group_9cj0g_8 {
4372
4431
  display: flex;
4373
4432
  flex-direction: column;
4374
4433
  gap: var(--space-8);
4375
4434
  }
4376
4435
 
4377
- ._title_t4qba_14 {
4436
+ ._title_9cj0g_14 {
4378
4437
  margin: 0;
4379
4438
  font-size: var(--type-trimmed-md-size);
4380
4439
  line-height: var(--type-trimmed-md-line);
@@ -4382,18 +4441,18 @@
4382
4441
  color: var(--color-text-default);
4383
4442
  }
4384
4443
 
4385
- ._body_t4qba_22 {
4444
+ ._body_9cj0g_22 {
4386
4445
  color: var(--color-text-subtle);
4387
4446
  }
4388
4447
 
4389
- ._paragraph_t4qba_26 {
4448
+ ._paragraph_9cj0g_26 {
4390
4449
  margin: 0;
4391
4450
  font-size: var(--type-paragraph-size);
4392
4451
  line-height: var(--type-paragraph-line);
4393
4452
  }
4394
4453
 
4395
4454
  /* paragraph-spacing/lg between paragraphs, nothing after the last. */
4396
- ._paragraph_t4qba_26 + ._paragraph_t4qba_26 {
4455
+ ._paragraph_9cj0g_26 + ._paragraph_9cj0g_26 {
4397
4456
  margin-top: var(--space-12);
4398
4457
  }
4399
4458
 
@@ -4406,12 +4465,14 @@
4406
4465
  * lines is exactly `font-size × line-height × 3`, so the cut lands on a line boundary
4407
4466
  * rather than through the middle of one.
4408
4467
  */
4409
- ._clamped_t4qba_46 {
4468
+ ._clamped_9cj0g_46 {
4410
4469
  max-height: calc(var(--type-paragraph-size) * var(--type-paragraph-line) * 3);
4411
4470
  overflow: hidden;
4412
4471
  }
4413
4472
 
4414
- ._toggle_t4qba_51 {
4473
+ ._toggle_9cj0g_51 {
4474
+ /* Anchors the hit-area pseudo-element below. */
4475
+ position: relative;
4415
4476
  align-self: flex-start;
4416
4477
  padding: 0;
4417
4478
  border: none;
@@ -4424,6 +4485,16 @@
4424
4485
  font-weight: var(--font-weight-bold);
4425
4486
  cursor: pointer;
4426
4487
  }
4488
+
4489
+ /*
4490
+ * 36x25 drawn, 44x45 tapped. The control sits in clear space — 266px above, 72px below —
4491
+ * so this cannot overlap anything.
4492
+ */
4493
+ ._toggle_9cj0g_51::after {
4494
+ content: '';
4495
+ position: absolute;
4496
+ inset: -10px -4px;
4497
+ }
4427
4498
  ._row_l4edh_1 {
4428
4499
  display: flex;
4429
4500
  flex-wrap: wrap;
package/dist/styles.css CHANGED
@@ -3647,14 +3647,14 @@
3647
3647
  gap: var(--space-20);
3648
3648
  }
3649
3649
 
3650
- ._gallery_10mch_1 {
3650
+ ._gallery_9e68b_1 {
3651
3651
  position: relative;
3652
3652
  display: flex;
3653
3653
  flex-direction: column;
3654
3654
  gap: var(--space-12);
3655
3655
  }
3656
3656
 
3657
- ._track_10mch_8 {
3657
+ ._track_9e68b_8 {
3658
3658
  display: flex;
3659
3659
  overflow-x: auto;
3660
3660
  scroll-snap-type: x mandatory;
@@ -3674,30 +3674,46 @@
3674
3674
  scrollbar-width: none;
3675
3675
  }
3676
3676
 
3677
- ._track_10mch_8::-webkit-scrollbar {
3677
+ ._track_9e68b_8::-webkit-scrollbar {
3678
3678
  display: none;
3679
3679
  }
3680
3680
 
3681
- ._slide_10mch_32 {
3681
+ ._slide_9e68b_32 {
3682
3682
  position: relative;
3683
3683
  flex: 0 0 100%;
3684
3684
  scroll-snap-align: center;
3685
3685
  aspect-ratio: 375 / 475;
3686
3686
  }
3687
3687
 
3688
- ._image_10mch_39 {
3688
+ ._image_9e68b_39 {
3689
3689
  object-fit: cover;
3690
3690
  }
3691
3691
 
3692
- ._placeholder_10mch_43 {
3692
+ ._placeholder_9e68b_43 {
3693
3693
  width: 100%;
3694
3694
  aspect-ratio: 375 / 475;
3695
3695
  background: var(--color-surface-soft);
3696
3696
  }
3697
3697
 
3698
3698
  /* Share button — top-right chrome over the image (mobile/button: 32px, soft, radius-8). */
3699
- ._share_10mch_50 {
3699
+ ._share_9e68b_50 {
3700
3700
  position: absolute;
3701
+ /*
3702
+ * **Without this the button is drawn and cannot be tapped.**
3703
+ *
3704
+ * `.share` is the gallery's first child and the image track follows it. Both the button
3705
+ * and the `fill` images inside the track are absolutely positioned and nothing here
3706
+ * declared a `z-index`, so painting order fell to DOM order and every image won. A tap
3707
+ * anywhere on the icon landed on the photograph — the control looked completely normal
3708
+ * and did nothing, which is the failure mode this repo has been bitten by before.
3709
+ *
3710
+ * The pagination dots were never affected: they come *after* the track, so they were
3711
+ * already on top by accident. That is why this survived review.
3712
+ *
3713
+ * Found by hit-testing `document.elementFromPoint` at the button's own centre, not by
3714
+ * reading the stylesheet — from the CSS it looks correct.
3715
+ */
3716
+ z-index: 1;
3701
3717
  top: var(--space-16);
3702
3718
  right: var(--space-16);
3703
3719
  display: inline-flex;
@@ -3713,13 +3729,25 @@
3713
3729
  cursor: pointer;
3714
3730
  }
3715
3731
 
3716
- ._dots_10mch_67 {
3732
+ ._dots_9e68b_83 {
3717
3733
  display: flex;
3718
3734
  justify-content: center;
3719
3735
  gap: var(--space-6);
3720
3736
  }
3721
3737
 
3722
- ._dot_10mch_67 {
3738
+ /*
3739
+ * The icon is drawn at 32px because that is the box the design gives it; the TAP is 44px.
3740
+ * Same trick as the header's controls — a negative-inset pseudo-element, so the hit area
3741
+ * grows and not one pixel moves. Measured 32px of clear space above it, so symmetric
3742
+ * expansion cannot reach the control overhead.
3743
+ */
3744
+ ._share_9e68b_50::after {
3745
+ content: '';
3746
+ position: absolute;
3747
+ inset: -6px;
3748
+ }
3749
+
3750
+ ._dot_9e68b_83 {
3723
3751
  width: 8px;
3724
3752
  height: 8px;
3725
3753
  padding: 0;
@@ -3731,13 +3759,28 @@
3731
3759
  position: relative;
3732
3760
  }
3733
3761
 
3734
- ._dot_10mch_67::after {
3762
+ /*
3763
+ * The hit area is tall, and only as wide as the dot's own column.
3764
+ *
3765
+ * It used to be `inset: -18px` on all four sides, which reads as "44px target" and behaved
3766
+ * as the opposite. The dots are 8px with a 6px gap — a 14px pitch — so an 18px horizontal
3767
+ * bleed put every dot's hit area completely over its neighbour's, and the later sibling
3768
+ * paints on top. **Only the last dot was tappable**; aiming at the first selected the
3769
+ * second. Measured by walking `elementFromPoint` outward from each dot's centre.
3770
+ *
3771
+ * 3px is half the gap, so the columns tile exactly and never overlap. That makes each dot
3772
+ * 14px wide, which is under the 44px guideline and is the widest the design's own spacing
3773
+ * allows without one dot stealing another's taps — a reliable 14x44 beats a nominal 44x44
3774
+ * that belongs to the wrong control. Swiping the track remains the primary affordance, and
3775
+ * it is unaffected.
3776
+ */
3777
+ ._dot_9e68b_83::after {
3735
3778
  content: '';
3736
3779
  position: absolute;
3737
- inset: -18px;
3780
+ inset: -18px -3px;
3738
3781
  }
3739
3782
 
3740
- ._dotActive_10mch_91 {
3783
+ ._dotActive_9e68b_134 {
3741
3784
  background: var(--color-surface-max);
3742
3785
  }
3743
3786
  /*
@@ -4115,18 +4158,18 @@
4115
4158
  animation: none;
4116
4159
  }
4117
4160
  }
4118
- ._root_9zj90_1 {
4161
+ ._root_6sbl9_1 {
4119
4162
  display: flex;
4120
4163
  flex-direction: column;
4121
4164
  gap: var(--space-12);
4122
4165
  }
4123
4166
 
4124
- ._header_9zj90_7 {
4167
+ ._header_6sbl9_7 {
4125
4168
  display: flex;
4126
4169
  align-items: center;
4127
4170
  }
4128
4171
 
4129
- ._title_9zj90_12 {
4172
+ ._title_6sbl9_12 {
4130
4173
  margin: 0;
4131
4174
  font-size: var(--type-trimmed-md-size);
4132
4175
  line-height: var(--type-trimmed-md-line);
@@ -4134,14 +4177,14 @@
4134
4177
  color: var(--color-text-default);
4135
4178
  }
4136
4179
 
4137
- ._chips_9zj90_20 {
4180
+ ._chips_6sbl9_20 {
4138
4181
  display: flex;
4139
4182
  flex-wrap: wrap;
4140
4183
  gap: var(--space-8);
4141
4184
  }
4142
4185
 
4143
4186
  /* 48px square chips per the mobile/pdp/size component (radius-xs, not radius-sm). */
4144
- ._chip_9zj90_20 {
4187
+ ._chip_6sbl9_20 {
4145
4188
  display: inline-flex;
4146
4189
  align-items: center;
4147
4190
  justify-content: center;
@@ -4158,7 +4201,7 @@
4158
4201
  cursor: pointer;
4159
4202
  }
4160
4203
 
4161
- ._selected_9zj90_44 {
4204
+ ._selected_6sbl9_44 {
4162
4205
  border-color: var(--color-surface-max);
4163
4206
  background: var(--color-surface-max);
4164
4207
  color: var(--color-text-strong);
@@ -4170,7 +4213,7 @@
4170
4213
  * range reads complete"), so a fan can see the range runs to XXL and that their size is
4171
4214
  * the one that has gone, rather than seeing a range with a hole in it.
4172
4215
  */
4173
- ._disabled_9zj90_56 {
4216
+ ._disabled_6sbl9_56 {
4174
4217
  cursor: not-allowed;
4175
4218
  border-style: dashed;
4176
4219
  color: var(--color-text-disabled);
@@ -4178,14 +4221,16 @@
4178
4221
 
4179
4222
  /* Figma: regular weight, critical token. Bold read as a second heading next to the
4180
4223
  16px "Size" label above it. */
4181
- ._error_9zj90_64 {
4224
+ ._error_6sbl9_64 {
4182
4225
  margin: 0;
4183
4226
  font-size: var(--type-trimmed-sm-size);
4184
4227
  font-weight: var(--font-weight-regular);
4185
4228
  color: var(--color-state-surface-critical-default);
4186
4229
  }
4187
4230
 
4188
- ._sizeGuide_9zj90_71 {
4231
+ ._sizeGuide_6sbl9_71 {
4232
+ /* Anchors the hit-area pseudo-element below. */
4233
+ position: relative;
4189
4234
  display: inline-flex;
4190
4235
  align-items: center;
4191
4236
  gap: var(--space-2);
@@ -4199,9 +4244,23 @@
4199
4244
  cursor: pointer;
4200
4245
  }
4201
4246
 
4202
- ._chevron_9zj90_85 {
4247
+ ._chevron_6sbl9_87 {
4203
4248
  display: block;
4204
4249
  }
4250
+
4251
+ /*
4252
+ * An 18px-tall text button is a 44px tap target.
4253
+ *
4254
+ * **Asymmetric on purpose.** There are only 11px of clear space between this row and the
4255
+ * size chips above it, so a symmetric -13px would put this control's hit area *over* the
4256
+ * chips — and a fan aiming at XL would open the size guide instead. It grows 8px up (3px
4257
+ * of the gap left clear) and 18px down, into empty space: 8 + 18 + 18 = 44.
4258
+ */
4259
+ ._sizeGuide_6sbl9_71::after {
4260
+ content: '';
4261
+ position: absolute;
4262
+ inset: -8px -8px -18px;
4263
+ }
4205
4264
  ._root_1h4ny_1 {
4206
4265
  display: flex;
4207
4266
  flex-direction: column;
@@ -4362,19 +4421,19 @@
4362
4421
  background: var(--color-surface-soft);
4363
4422
  }
4364
4423
  /* Figma mobile/pdp/details: heading+body group, then more/Less 4px under it. */
4365
- ._root_t4qba_2 {
4424
+ ._root_9cj0g_2 {
4366
4425
  display: flex;
4367
4426
  flex-direction: column;
4368
4427
  gap: var(--space-4);
4369
4428
  }
4370
4429
 
4371
- ._group_t4qba_8 {
4430
+ ._group_9cj0g_8 {
4372
4431
  display: flex;
4373
4432
  flex-direction: column;
4374
4433
  gap: var(--space-8);
4375
4434
  }
4376
4435
 
4377
- ._title_t4qba_14 {
4436
+ ._title_9cj0g_14 {
4378
4437
  margin: 0;
4379
4438
  font-size: var(--type-trimmed-md-size);
4380
4439
  line-height: var(--type-trimmed-md-line);
@@ -4382,18 +4441,18 @@
4382
4441
  color: var(--color-text-default);
4383
4442
  }
4384
4443
 
4385
- ._body_t4qba_22 {
4444
+ ._body_9cj0g_22 {
4386
4445
  color: var(--color-text-subtle);
4387
4446
  }
4388
4447
 
4389
- ._paragraph_t4qba_26 {
4448
+ ._paragraph_9cj0g_26 {
4390
4449
  margin: 0;
4391
4450
  font-size: var(--type-paragraph-size);
4392
4451
  line-height: var(--type-paragraph-line);
4393
4452
  }
4394
4453
 
4395
4454
  /* paragraph-spacing/lg between paragraphs, nothing after the last. */
4396
- ._paragraph_t4qba_26 + ._paragraph_t4qba_26 {
4455
+ ._paragraph_9cj0g_26 + ._paragraph_9cj0g_26 {
4397
4456
  margin-top: var(--space-12);
4398
4457
  }
4399
4458
 
@@ -4406,12 +4465,14 @@
4406
4465
  * lines is exactly `font-size × line-height × 3`, so the cut lands on a line boundary
4407
4466
  * rather than through the middle of one.
4408
4467
  */
4409
- ._clamped_t4qba_46 {
4468
+ ._clamped_9cj0g_46 {
4410
4469
  max-height: calc(var(--type-paragraph-size) * var(--type-paragraph-line) * 3);
4411
4470
  overflow: hidden;
4412
4471
  }
4413
4472
 
4414
- ._toggle_t4qba_51 {
4473
+ ._toggle_9cj0g_51 {
4474
+ /* Anchors the hit-area pseudo-element below. */
4475
+ position: relative;
4415
4476
  align-self: flex-start;
4416
4477
  padding: 0;
4417
4478
  border: none;
@@ -4424,6 +4485,16 @@
4424
4485
  font-weight: var(--font-weight-bold);
4425
4486
  cursor: pointer;
4426
4487
  }
4488
+
4489
+ /*
4490
+ * 36x25 drawn, 44x45 tapped. The control sits in clear space — 266px above, 72px below —
4491
+ * so this cannot overlap anything.
4492
+ */
4493
+ ._toggle_9cj0g_51::after {
4494
+ content: '';
4495
+ position: absolute;
4496
+ inset: -10px -4px;
4497
+ }
4427
4498
  ._row_l4edh_1 {
4428
4499
  display: flex;
4429
4500
  flex-wrap: wrap;
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "@nok-integration/storefront-react",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "description": "Mountable React storefront components: catalogue, product detail and cart, mounted directly into a host DOM tree. No iframe.",
5
5
  "license": "UNLICENSED",
6
- "publishConfig": {
7
- "access": "public",
8
- "registry": "https://registry.npmjs.org/"
9
- },
10
6
  "type": "module",
11
7
  "main": "./dist/index.mjs",
8
+ "module": "./dist/index.mjs",
12
9
  "types": "./dist/index.d.ts",
13
10
  "exports": {
14
11
  ".": {
@@ -23,32 +20,18 @@
23
20
  "dist",
24
21
  "!dist/**/*.map"
25
22
  ],
26
- "scripts": {
27
- "build": "pnpm build:package && pnpm build:standalone",
28
- "build:package": "vite build",
29
- "build:standalone": "BUILD_TARGET=standalone vite build",
30
- "typecheck": "tsc --noEmit",
31
- "build:partner": "PARTNER_DROP=1 pnpm build && pnpm pack"
32
- },
23
+ "sideEffects": [
24
+ "*.css"
25
+ ],
33
26
  "peerDependencies": {
34
27
  "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
35
28
  "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
36
29
  },
37
- "devDependencies": {
38
- "@raqmix/contracts": "^2.0.0",
39
- "@microsoft/api-extractor": "^7.58.12",
40
- "@types/react": "^19.1.0",
41
- "@types/react-dom": "^19.1.0",
42
- "@vitejs/plugin-react": "^4.3.4",
43
- "typescript": "^5.7.3",
44
- "vite": "^7.0.0",
45
- "vite-plugin-dts": "^5.0.3"
46
- },
47
- "module": "./dist/index.mjs",
48
- "sideEffects": [
49
- "*.css"
50
- ],
51
30
  "dependencies": {
52
31
  "zod": "^3.24.1"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "registry": "https://registry.npmjs.org/"
53
36
  }
54
37
  }