@bytebrand/fe-ui-core-autobahn 1.0.108 → 1.0.109

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/cards/CarRow.tsx CHANGED
@@ -250,6 +250,7 @@ const CarRow: React.FunctionComponent<CarRowProps> = ({
250
250
  {car.rating && (
251
251
  <StatChip tone={ratingTone(car.rating)} icon={ratingTone(car.rating) === 'good' ? 'check' : 'minus'}>
252
252
  {t('autobahn:tier.' + car.rating)}
253
+ {ratingTone(car.rating) === 'good' && <sup>4</sup>}
253
254
  </StatChip>
254
255
  )}
255
256
  </div>
@@ -296,6 +297,7 @@ const CarRow: React.FunctionComponent<CarRowProps> = ({
296
297
  {car.rating && (
297
298
  <StatChip tone={ratingTone(car.rating)} icon={ratingTone(car.rating) === 'good' ? 'check' : 'minus'}>
298
299
  {t('autobahn:tier.' + car.rating)}
300
+ {ratingTone(car.rating) === 'good' && <sup>4</sup>}
299
301
  </StatChip>
300
302
  )}
301
303
  <div style={{ flex: 1 }}>
@@ -600,7 +600,7 @@ export const PriceHero: React.FunctionComponent<PriceHeroProps> = ({ car, mode,
600
600
  <div className={'pp-hero' + (isMoney ? ' pp-hero--buy' : '')}>
601
601
  <div className="pp-hero-rate">
602
602
  <b className={'num pp-hero-val' + rateLenClass + buyLen6Class}>
603
- {isMoney ? value.toLocaleString('de-DE') : fmtRate(value)} €
603
+ {isMoney ? value.toLocaleString('de-DE') : fmtRate(value)} €{isMoney && <sup>1</sup>}
604
604
  </b>
605
605
  {/* z.B./old-rate/unit grouped in one stacked column beside the hero figure — mirrors
606
606
  the VDP finance-calculator's .fin-rate-unit layout (FinanceCalculator.tsx) exactly.
@@ -900,28 +900,60 @@ interface PpRowProps {
900
900
  focus?: boolean;
901
901
  /** format as a full euro price (Kaufen) instead of a rounded rate. */
902
902
  money?: boolean;
903
+ t: TranslationFunction;
903
904
  }
904
905
 
905
906
  /**
906
907
  * One payment-path row: label (+ a quiet info marker) on the left, the price right-aligned
907
908
  * with an optional struck-through previous price above it. Even row spacing reproduces the
908
- * production "Finanzierung / Leasing / Kaufen" price table the design references.
909
+ * production "Finanzierung / Leasing / Kaufen" price table the design references. The struck
910
+ * `old` price gets the same "²" UvpBadge + click-to-open popover as PriceHero's own Kaufen
911
+ * old-price (same n, same body/close copy) — one-to-one, not a re-styled variant.
909
912
  */
910
- const PpRow: React.FunctionComponent<PpRowProps> = ({ label, value, unit, old, focus, money }) => (
911
- <div className={'pp-row' + (focus ? ' pp-row--focus' : '')}>
912
- <span className="pp-row-label">
913
- {label}
914
- <Icon name="info" size={13} stroke={1.9} cls="pp-row-i" />
915
- </span>
916
- <span className="pp-row-price">
917
- {old != null && <span className="pp-old">{old.toLocaleString('de-DE')} €</span>}
918
- <span className="pp-now">
919
- {unit && <span className="pp-unit">{unit}</span>}
920
- <b className="num pp-val">{money ? value.toLocaleString('de-DE') : fmtRate(value)} €</b>
913
+ const PpRow: React.FunctionComponent<PpRowProps> = ({ label, value, unit, old, focus, money, t }) => {
914
+ const [uvpTooltipOpen, setUvpTooltipOpen] = useState(false);
915
+ // 5-plus-digit prices (e.g. 60.248 €) shrink to keep the row from crowding — 4-digit values
916
+ // stay at the base size. Same digit-length technique as PriceHero's buyLen6Class.
917
+ const valDigits = String(Math.round(value)).length;
918
+ const valLenClass = valDigits >= 5 ? ' pp-val--len5' : '';
919
+ return (
920
+ <div className={'pp-row' + (focus ? ' pp-row--focus' : '')}>
921
+ <span className="pp-row-label">
922
+ {label}
923
+ <Icon name="info" size={13} stroke={1.9} cls="pp-row-i" />
921
924
  </span>
922
- </span>
923
- </div>
924
- );
925
+ <span className="pp-row-price">
926
+ {old != null && (
927
+ <span
928
+ className="pp-old"
929
+ role="button"
930
+ tabIndex={0}
931
+ onClick={(e) => {
932
+ e.stopPropagation();
933
+ setUvpTooltipOpen((v) => !v);
934
+ }}
935
+ >
936
+ {old.toLocaleString('de-DE')} €
937
+ <UvpBadge
938
+ n={2}
939
+ open={uvpTooltipOpen}
940
+ onToggle={() => setUvpTooltipOpen((v) => !v)}
941
+ onClose={() => setUvpTooltipOpen(false)}
942
+ closeLabel={t('autobahn:modal.close')}
943
+ body={t('autobahn:card.price.uvpListTooltipBody')}
944
+ />
945
+ </span>
946
+ )}
947
+ <span className="pp-now">
948
+ {unit && <span className="pp-unit">{unit}</span>}
949
+ <b className={'num pp-val' + valLenClass}>
950
+ {money ? value.toLocaleString('de-DE') : fmtRate(value)} €{money && <sup>1</sup>}
951
+ </b>
952
+ </span>
953
+ </span>
954
+ </div>
955
+ );
956
+ };
925
957
 
926
958
  /**
927
959
  * Monthly-rate payment paths (SRP price column, TOP group) — modelled on the legacy design:
@@ -978,7 +1010,7 @@ export const PricePaths: React.FunctionComponent<PricePathsProps> = ({ car, t, c
978
1010
  </div>
979
1011
  {hasLease && (
980
1012
  <div className="pp-lease-row">
981
- <PpRow label={t('autobahn:card.price.leasing')} value={car.lease as number} unit="mtl." />
1013
+ <PpRow label={t('autobahn:card.price.leasing')} value={car.lease as number} unit="mtl." t={t} />
982
1014
  {hasLeasTerms && (
983
1015
  <div className="pp-sub-terms">
984
1016
  {/* dedicated keys (not a rework of leaseTerms) so mobile/VDP's own reviewed
@@ -1009,7 +1041,7 @@ export const PricePaths: React.FunctionComponent<PricePathsProps> = ({ car, t, c
1009
1041
  export const PriceKauf: React.FunctionComponent<PricePathsProps> = ({ car, t }) => {
1010
1042
  const showStrike = car.savings != null && car.savings > 600 && car.listPrice != null;
1011
1043
  return (
1012
- <PpRow label={t('autobahn:card.price.buy')} value={car.price} old={showStrike ? (car.listPrice as number) : null} money />
1044
+ <PpRow label={t('autobahn:card.price.buy')} value={car.price} old={showStrike ? (car.listPrice as number) : null} money t={t} />
1013
1045
  );
1014
1046
  };
1015
1047
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/fe-ui-core-autobahn",
3
- "version": "1.0.108",
3
+ "version": "1.0.109",
4
4
  "description": "auto.de md3 (Autobahn) design system — UI primitives, cards, chrome, VDP modules, theme CSS and locales. Ships raw TypeScript source (like fe-ui-core before it); each consumer's bundler compiles it.",
5
5
  "main": "index.ts",
6
6
  "license": "UNLICENSED",
package/theme/md3.css CHANGED
@@ -55,10 +55,12 @@
55
55
  --md-secondary-container: #e7e9f1;
56
56
  --md-on-secondary-container: #3a4150;
57
57
 
58
- /* price-rating + deal accent (green) */
58
+ /* price-rating + deal accent (green) — same "good" tier green (TIER_COLORS[0], #168A45) and
59
+ 16%-tint background as .fin-eval-pill--good (FinanceCalculator/VdpModules), so the
60
+ search-list/VDP-header StatChip and the finance-card rating pill read as the same color. */
59
61
  --md-good: #0f7b43;
60
- --md-good-container: #c8efcf;
61
- --md-on-good-container: #04391f;
62
+ --md-good-container: color-mix(in srgb, #168a45 16%, transparent);
63
+ --md-on-good-container: #168a45;
62
64
  --md-warn-container: #fce3b4;
63
65
  --md-on-warn-container: #4a3204;
64
66
  --md-error: #ba1a1a;
@@ -437,6 +439,16 @@ line-height: 31px;
437
439
  background: var(--md-warn-container);
438
440
  color: var(--md-on-warn-container);
439
441
  }
442
+ /* "4" footnote (VDP header's "Guter Preis" chip) — .statchip is a flex container like
443
+ .fin-eval-pill, so the same position:relative/top lift technique applies, just re-tuned for
444
+ this chip's smaller 12px text. */
445
+ .md3 .statchip sup {
446
+ position: relative;
447
+ top: -3px;
448
+ left: -3px;
449
+ font-size: 8px;
450
+ line-height: 8px;
451
+ }
440
452
  .md3 .statchip .lucide {
441
453
  width: 14px;
442
454
  height: 14px;
@@ -4503,11 +4515,15 @@ line-height: 31px;
4503
4515
  top: -7px;
4504
4516
  right: -7px;
4505
4517
  }
4518
+ /* positioned against .pp-eyebrow (line ~4476) — a tiny (8px text) span, so stretching between
4519
+ left/right offsets alone (as this used to do) computed a near-zero box width and forced the
4520
+ body text into a tall, narrow, wrapped column. Explicit width, same as the .cc-price/
4521
+ .fin-calc-card/.listcard-price .pp-old tooltip elsewhere, fixes that. */
4506
4522
  .md3 .listcard-price .pricepaths .pp-uvp-tooltip {
4507
4523
  position: absolute;
4508
- top: 6px;
4509
- right: -18px;
4510
- left: -18px;
4524
+ top: 9px;
4525
+ right: -35px;
4526
+ width: 281px;
4511
4527
  z-index: 20;
4512
4528
  padding: 10px 12px;
4513
4529
  border-radius: 15px;
@@ -4608,7 +4624,7 @@ line-height: 31px;
4608
4624
  display: flex;
4609
4625
  flex-direction: row;
4610
4626
  align-items: baseline;
4611
- gap: 8px;
4627
+ gap: 13px;
4612
4628
  line-height: 1.08;
4613
4629
  min-width: 0;
4614
4630
  }
@@ -4634,6 +4650,27 @@ line-height: 31px;
4634
4650
  letter-spacing: -0.4px;
4635
4651
  white-space: nowrap;
4636
4652
  }
4653
+ /* 5-plus-digit price (e.g. 60.248 €) shrinks so it doesn't crowd the row — 4-digit values keep
4654
+ the base 19px above untouched, same digit-length technique as .pp-hero-val--buy-len6/7. */
4655
+ .md3 .pp-val.pp-val--len5 {
4656
+ font-size: 16px;
4657
+ }
4658
+ /* Kaufen row's "1" price footnote marker (desktop .pricepaths stacked rows, PpRow money=true
4659
+ only) — same small/quiet sup treatment as the card's other footnote markers (.fin-eval-pill
4660
+ sup, .fin-rate-main sup, etc.): vertical-align:super does the actual lift, position:relative
4661
+ top just fine-tunes it for this specific 19px price size. */
4662
+ .md3 .pp-val sup {
4663
+ position: relative;
4664
+ top: 4px;
4665
+ left: 1px;
4666
+ vertical-align: super;
4667
+ font-family: 'Arial', Helvetica, sans-serif;
4668
+ font-style: normal;
4669
+ font-weight: 400;
4670
+ font-size: 8px;
4671
+ line-height: 9px;
4672
+ color: #111820;
4673
+ }
4637
4674
  /* financing is the ONLY blue, dominant figure — big hero rate */
4638
4675
  .md3 .pp-row--focus .pp-row-label {
4639
4676
  color: var(--md-on-surface);
@@ -4854,13 +4891,22 @@ line-height: 31px;
4854
4891
  color: #0e2238;
4855
4892
  margin-bottom: 2px;
4856
4893
  }
4894
+ /* .pp-old itself needs to be the positioned ancestor for its badge below (same reasoning as
4895
+ .cc-price/.fin-calc-card's own scoped `.pp-hero-old { position: relative }` override) —
4896
+ the base .pp-old rule has no position, so without this the badge would anchor to whatever
4897
+ positioned ancestor happens to be further up instead of sitting on the price text itself. */
4898
+ .md3 .listcard-price .pp-old {
4899
+ position: relative;
4900
+ }
4857
4901
  /* footnote badge (small bordered circle, not a plain <sup>) pinned to the eyebrow's top-right
4858
4902
  corner — absolute against .pp-eyebrow's own position:relative above. Shared by the CarCard
4859
- hero (.cc-price) and the VDP FinanceCalculator's own rate example (.fin-calc-card). */
4903
+ hero (.cc-price), the VDP FinanceCalculator's own rate example (.fin-calc-card), and
4904
+ CarRow's desktop stacked Kaufen row (.listcard-price .pp-old, PpRow's "2" badge). */
4860
4905
  .md3 .cc-price .pp-eyebrow-badge,
4861
4906
  .md3 .fin-calc-card .pp-eyebrow-badge,
4862
4907
  .md3 .vdp-bottom-bar .pp-eyebrow-badge,
4863
- .md3 .fin-modal .pp-eyebrow-badge {
4908
+ .md3 .fin-modal .pp-eyebrow-badge,
4909
+ .md3 .listcard-price .pp-old .pp-eyebrow-badge {
4864
4910
  display: inline-flex;
4865
4911
  align-items: center;
4866
4912
  justify-content: center;
@@ -4881,6 +4927,11 @@ line-height: 31px;
4881
4927
  color: #0e2238;
4882
4928
  cursor: pointer;
4883
4929
  }
4930
+ /* CarRow's desktop stacked Kaufen row sits closer to the price text than the card contexts
4931
+ above, so the shared -8px offset sits a touch too far right. */
4932
+ .md3 .listcard-price .pp-old .pp-eyebrow-badge {
4933
+ right: -10px;
4934
+ }
4884
4935
  /* mobile-only nudge: same badge, tucked slightly closer to the eyebrow text on narrow cards. */
4885
4936
  @media (max-width: 1024px) {
4886
4937
  .md3 .cc-price .pp-eyebrow-badge,
@@ -4899,11 +4950,14 @@ line-height: 31px;
4899
4950
  top: -7px;
4900
4951
  right: -12px;
4901
4952
  }
4902
- /* "3" footnote popover — click the badge above to open, the × or an outside click to close. */
4953
+ /* "3" footnote popover — click the badge above to open, the × or an outside click to close.
4954
+ Also covers CarRow's desktop stacked Kaufen row (.listcard-price .pp-old, PpRow's "2"
4955
+ badge) — same popover, reused rather than re-styled. */
4903
4956
  .md3 .cc-price .pp-uvp-tooltip,
4904
4957
  .md3 .fin-calc-card .pp-uvp-tooltip,
4905
4958
  .md3 .vdp-bottom-bar .pp-uvp-tooltip,
4906
- .md3 .fin-modal .pp-uvp-tooltip {
4959
+ .md3 .fin-modal .pp-uvp-tooltip,
4960
+ .md3 .listcard-price .pp-old .pp-uvp-tooltip {
4907
4961
  position: absolute;
4908
4962
  top: 6px;
4909
4963
  right: -84px;
@@ -4922,6 +4976,12 @@ line-height: 31px;
4922
4976
  top: -20px;
4923
4977
  right: -110px;
4924
4978
  }
4979
+ /* CarRow's desktop stacked Kaufen row is narrower still (.pp-old sits inline in the price
4980
+ row, not a full-width card), so the shared -84px offset overhangs it — pull it in closer. */
4981
+ .md3 .listcard-price .pp-old .pp-uvp-tooltip {
4982
+ top: 6px;
4983
+ right: -40px;
4984
+ }
4925
4985
  /* .fin-calc-card is narrower than .cc-price / .fin-modal, so the shared -84px offset lets
4926
4986
  the popover overhang the card's right edge — this rule itself is unscoped (applies at
4927
4987
  every width, see the file-wide note above), so the max-width:600px block right below
@@ -4950,7 +5010,8 @@ line-height: 31px;
4950
5010
  .md3 .cc-price .pp-uvp-tooltip-close,
4951
5011
  .md3 .fin-calc-card .pp-uvp-tooltip-close,
4952
5012
  .md3 .vdp-bottom-bar .pp-uvp-tooltip-close,
4953
- .md3 .fin-modal .pp-uvp-tooltip-close {
5013
+ .md3 .fin-modal .pp-uvp-tooltip-close,
5014
+ .md3 .listcard-price .pp-old .pp-uvp-tooltip-close {
4954
5015
  position: absolute;
4955
5016
  top: -38px;
4956
5017
  right: -35px;
@@ -4968,7 +5029,8 @@ line-height: 31px;
4968
5029
  .md3 .cc-price .pp-uvp-tooltip-body,
4969
5030
  .md3 .fin-calc-card .pp-uvp-tooltip-body,
4970
5031
  .md3 .vdp-bottom-bar .pp-uvp-tooltip-body,
4971
- .md3 .fin-modal .pp-uvp-tooltip-body {
5032
+ .md3 .fin-modal .pp-uvp-tooltip-body,
5033
+ .md3 .listcard-price .pp-old .pp-uvp-tooltip-body {
4972
5034
  font-family: Arial, Helvetica, sans-serif;
4973
5035
  font-weight: 400;
4974
5036
  font-size: 11px;
@@ -5075,6 +5137,21 @@ line-height: 31px;
5075
5137
  font-size: 43px;
5076
5138
  line-height: 47px;
5077
5139
  }
5140
+ /* CarCard Kaufen hero "1" price footnote marker — same treatment as
5141
+ .fin-rate-main .num.hide-mobile sup (see the desktop .cc-price .pp-hero--buy .pp-hero-val
5142
+ block below for the matching rule at that breakpoint). */
5143
+ .md3 .cc-price .pp-hero--buy .pp-hero-val sup {
5144
+ position: relative;
5145
+ top: 2px;
5146
+ left: 3px;
5147
+ vertical-align: super;
5148
+ font-family: Arial, Helvetica, sans-serif;
5149
+ font-style: normal;
5150
+ font-weight: 400;
5151
+ font-size: 8px;
5152
+ line-height: 9px;
5153
+ color: #111820;
5154
+ }
5078
5155
  /* Preisbewertung pill + segmented tier bar, grouped in one column exactly like the VDP
5079
5156
  finance-calculator's MOBILE .fin-eval-col (FinanceCalculator.tsx) — replaces the old
5080
5157
  StatChip pill + full-width gradient .pp-bar (see PriceEvalCol in cardParts.tsx). No
@@ -10432,6 +10509,21 @@ color: #005CFF;
10432
10509
  font-size: 43px;
10433
10510
  line-height: 47px;
10434
10511
  }
10512
+ /* CarCard Kaufen hero "1" price footnote marker — same treatment as
10513
+ .fin-rate-main .num.hide-mobile sup, not scoped to a media query since the same offset
10514
+ reads fine against both the mobile and desktop .pp-hero--buy .pp-hero-val sizes above. */
10515
+ .md3 .cc-price .pp-hero--buy .pp-hero-val sup {
10516
+ position: relative;
10517
+ top: 2px;
10518
+ left: 3px;
10519
+ vertical-align: super;
10520
+ font-family: Arial, Helvetica, sans-serif;
10521
+ font-style: normal;
10522
+ font-weight: 400;
10523
+ font-size: 8px;
10524
+ line-height: 9px;
10525
+ color: #111820;
10526
+ }
10435
10527
 
10436
10528
  /* Finanzierung/Leasing/Kaufen tabs — Figma: only the ACTIVE tab is bold + Deep Navy #0e2238
10437
10529
  inactive text (the shared .fin-tab rule bolds both and uses #111820, since the VDP
@@ -100,6 +100,7 @@ export const VdpHeaderSummary: React.FunctionComponent<VdpHeaderCardProps> = ({
100
100
  icon={vehicle.rating === 'fair' ? 'minus' : 'check'}
101
101
  >
102
102
  {t('autobahn:tier.' + vehicle.rating)}
103
+ {vehicle.rating !== 'fair' && <sup>4</sup>}
103
104
  </StatChip>
104
105
  )}
105
106
  </div>
@@ -3078,7 +3079,6 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
3078
3079
  <div className="fin-sheet-terms">
3079
3080
  <div className="md-title-sm" style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
3080
3081
  {t('autobahn:finance.termSelectionMonths')}
3081
- <Icon name="info" size={13} color="var(--md-on-surface-variant)" />
3082
3082
  </div>
3083
3083
  <TermChips
3084
3084
  options={termVals.filter((v) => v !== 12 && v !== 96)}