@bytebrand/fe-ui-core-autobahn 1.0.21 → 1.0.22

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.
@@ -14,6 +14,7 @@
14
14
  "leaseTerms": "{{mileage}} km/Jahr · {{months}} Monate",
15
15
  "purchasePrice": "Kaufpreis",
16
16
  "priceDropAmount": "−{{amount}} €",
17
+ "priceDropSave": "{{amount}} € sparen",
17
18
  "savedAmount": "{{amount}} € gespart",
18
19
  "zeroDownCompact": "0 € Anz.",
19
20
  "zeroDown": "0 € Anzahlung",
@@ -414,7 +415,9 @@
414
415
  "back": "zurück",
415
416
  "share": "teilen",
416
417
  "photos": "Alle {{count}} Fotos",
417
- "photos_plural": "Alle {{count}} Fotos"
418
+ "photos_plural": "Alle {{count}} Fotos",
419
+ "photosCount": "{{count}} Fotos",
420
+ "photosCount_plural": "{{count}} Fotos"
418
421
  },
419
422
  "favorite": {
420
423
  "label": "merken"
@@ -14,6 +14,7 @@
14
14
  "leaseTerms": "{{mileage}} km/year · {{months}} months",
15
15
  "purchasePrice": "Purchase price",
16
16
  "priceDropAmount": "−{{amount}} €",
17
+ "priceDropSave": "Save {{amount}} €",
17
18
  "savedAmount": "€{{amount}} saved",
18
19
  "zeroDownCompact": "€0 down",
19
20
  "zeroDown": "€0 down payment",
@@ -414,7 +415,9 @@
414
415
  "back": "Back",
415
416
  "share": "Share",
416
417
  "photos": "All {{count}} photos",
417
- "photos_plural": "All {{count}} photos"
418
+ "photos_plural": "All {{count}} photos",
419
+ "photosCount": "{{count}} photos",
420
+ "photosCount_plural": "{{count}} photos"
418
421
  },
419
422
  "favorite": {
420
423
  "label": "Save"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/fe-ui-core-autobahn",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
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
@@ -155,12 +155,12 @@
155
155
  NOT the 38px tappable filter-chip the base .chip is. */
156
156
  .md3 .listcard-chips .chip { height: 24px; padding: 0 10px; font-size: 11.5px; border-radius: 8px; cursor: default; }
157
157
  .md3 .listcard-chips .chip:hover { background: var(--md-surface); border-color: var(--md-outline-variant); }
158
- /* VDP trust row (Sofort verfügbar/views/favorites/Geprüft) — same compact non-interactive
159
- sizing as the listcard equipment tags, standalone (not nested under .listcard-chips). */
160
- .md3 .vdp-trust-chip { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px;
161
- border-radius: 8px; font-size: 11.5px; font-weight: 500; line-height: 1; cursor: default; white-space: nowrap;
162
- background: var(--md-surface); color: var(--md-on-surface-variant); border: 1px solid var(--md-outline-variant); }
163
- .md3 .vdp-trust-chip .lucide { width: 14px; height: 14px; flex: none; }
158
+ /* VDP trust row (Sofort verfügbar/views/favorites/Geprüft) — mobile-only, so the figma
159
+ push-chip palette is used literally (light blue-gray fill, dark bold label). */
160
+ .md3 .vdp-trust-chip { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 11px;
161
+ border-radius: 11px; font-size: 11px; font-weight: 700; line-height: 1; cursor: default; white-space: nowrap;
162
+ background: #F5F7FA; color: #1D2A38; border: 1px solid #E5EAF0; }
163
+ .md3 .vdp-trust-chip .lucide { width: 16px; height: 16px; flex: none; }
164
164
 
165
165
  /* status / rating chip (no CTA) */
166
166
  .md3 .statchip { display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 10px;
@@ -174,12 +174,17 @@
174
174
  border-radius: var(--md-r-full); background: rgba(0,0,0,.55); color: #fff; font-size: 12px; font-weight: 500; backdrop-filter: blur(2px); }
175
175
  .md3 .imglabel .lucide { width: 14px; height: 14px; }
176
176
 
177
- /* mobile-only row under the gallery image: photo-count (plain text link, not an overlay
178
- pill — the dark .imglabel pill only makes sense against the image itself) · favorite/share. */
179
- .md3 .gallery-controls-row { display: flex; align-items: center; justify-content: space-between; padding: 10px 2px 0; }
180
- .md3 .gallery-photos-btn { display: inline-flex; align-items: center; gap: 6px; border: 0; background: none;
181
- cursor: pointer; font: inherit; font-size: 13px; font-weight: 600; color: var(--md-on-surface-variant); padding: 0; }
182
- .md3 .gallery-photos-btn .lucide { width: 15px; height: 15px; }
177
+ /* mobile-only row under the gallery image (mockup): bordered "X Fotos" pill · slider dots ·
178
+ share/favorite — all in ONE row in normal flow, nothing overlaid on the image. */
179
+ .md3 .gallery-controls-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 2px 0; }
180
+ .md3 .gallery-photos-btn { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; font: inherit;
181
+ font-size: 12px; font-weight: 700; color: var(--md-on-surface); height: 34px; padding: 0 13px; flex: none;
182
+ background: #fff; border: 1px solid #D4DDE7; border-radius: 16px; }
183
+ .md3 .gallery-photos-btn .lucide { width: 17px; height: 17px; }
184
+ /* page-driven slider dots (the carousel's own overlay dots are hidden on the phone VDP) */
185
+ .md3 .gallery-dots { display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; }
186
+ .md3 .gallery-dots .gdot { width: 6px; height: 6px; border-radius: 3px; background: #D4DDE7; flex: none; }
187
+ .md3 .gallery-dots .gdot--on { width: 30px; height: 5px; background: var(--md-on-surface); }
183
188
 
184
189
  /* ---------- cards ---------- */
185
190
  .md3 .mcard { background: var(--md-sc-lowest); border-radius: var(--md-r-md); border: 1px solid var(--md-outline-variant);
@@ -1463,13 +1468,29 @@
1463
1468
  display: flex; align-items: center; justify-content: center; rotate: 45deg; box-shadow: var(--md-e2); }
1464
1469
  .md3 .dealer-pin .lucide { rotate: -45deg; }
1465
1470
 
1466
- /* VDP: floating back button over the gallery — phone only (breadcrumb is hidden there) */
1471
+ /* VDP: floating back button over the gallery — hidden everywhere now (the phone mockup
1472
+ shows a clean image with only the nav arrows; desktop has the breadcrumb). */
1467
1473
  .md3 .vdp-back { display: none; position: absolute; top: 12px; left: 14px; z-index: 3; }
1468
1474
 
1475
+ /* mobile push-badges row ABOVE the gallery (mockup): flat tinted pills in normal flow —
1476
+ replaces the white overlay chips (DropChip & co) that sat on the image. Figma palette
1477
+ used literally; one non-wrapping swipeable line. */
1478
+ .md3 .vdp-push-badges { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; overflow-x: auto;
1479
+ scrollbar-width: none; margin-bottom: 10px; }
1480
+ .md3 .vdp-push-badges::-webkit-scrollbar { display: none; }
1481
+ .md3 .push-badge { display: inline-flex; align-items: center; justify-content: center; height: 28px; padding: 0 13px;
1482
+ border-radius: 11px; font-size: 13.5px; font-weight: 700; line-height: 1; white-space: nowrap; flex: none; }
1483
+ .md3 .push-badge--save { background: #E6F7EE; color: #168A45; border: 1px solid rgba(22,138,69,.07); }
1484
+ .md3 .push-badge--discount { background: #E60000; color: #fff; }
1485
+ .md3 .push-badge--down { background: #EAF2FF; color: #005CFF; border: 1px solid rgba(0,92,255,.05); }
1486
+
1469
1487
  @media (max-width: 600px) {
1470
1488
  /* full-bleed gallery: edge-to-edge, app-like (production pattern) */
1471
- .md3 .vdp-back { display: flex; }
1472
- .md3 .vdp-gallerybox { margin: -14px -24px 0; border-radius: 0; border: 0; box-shadow: none; }
1489
+ .md3 .vdp-gallerybox { margin: 0 -24px; border-radius: 0; border: 0; box-shadow: none; }
1490
+ /* the carousel's own overlay dots move into the below-image controls row (page-rendered) */
1491
+ .md3 .vdp-gallerybox .cardcar-dots { display: none; }
1492
+ /* mockup hero rate is a notch bigger on phone (inline style says 48 — hence !important) */
1493
+ .md3 .fin-rate-main .num { font-size: 52px !important; letter-spacing: -1.5px !important; }
1473
1494
  }
1474
1495
 
1475
1496
  /* ---------- save-search bar ---------- */
@@ -99,6 +99,7 @@ const CardCarousel: React.FunctionComponent<CardCarouselProps> = ({
99
99
  <Icon name="chevron-right" size={20} />
100
100
  </button>
101
101
  <div
102
+ className="cardcar-dots"
102
103
  style={{
103
104
  position: 'absolute',
104
105
  bottom: 9,
@@ -312,7 +312,17 @@ const FinanceCalculator: React.FunctionComponent<FinanceCalculatorProps> = ({
312
312
  className={'fin-tab' + (mode === k ? ' on' : '')}
313
313
  onClick={() => onModeChange(k)}
314
314
  >
315
- {t(labelKey)}
315
+ {k === 'cash' ? (
316
+ <>
317
+ {/* mockup names the cash tab "Kaufen" on phone; desktop keeps "Barzahlung" */}
318
+ <span className="hide-mobile">{t(labelKey)}</span>
319
+ <span className="show-mobile" style={{ justifyContent: 'center' }}>
320
+ {t('autobahn:card.price.buy')}
321
+ </span>
322
+ </>
323
+ ) : (
324
+ t(labelKey)
325
+ )}
316
326
  </button>
317
327
  ))}
318
328
  </div>
@@ -469,34 +479,30 @@ const FinanceCalculator: React.FunctionComponent<FinanceCalculatorProps> = ({
469
479
 
470
480
  {/* ============================== MOBILE (≤600px) — new layout ============================== */}
471
481
  <div className="show-mobile" style={{ flexDirection: 'column' }}>
472
- {/* side-by-side CTAs */}
473
- <div style={{ display: 'flex', gap: 10, marginTop: 14 }}>
474
- <Button variant="filled" size="lg" icon="shield-check" style={{ flex: 1 }} onClick={onSecure}>
482
+ {/* side-by-side CTAs — mockup ratio ~2:1, with the tiny auto.de-ID sitting
483
+ directly under the Anrufen button (not in its own full-width row). */}
484
+ <div style={{ display: 'flex', gap: 10, marginTop: 14, alignItems: 'flex-start' }}>
485
+ <Button variant="filled" size="lg" icon="shield-check" style={{ flex: 2 }} onClick={onSecure}>
475
486
  {t('autobahn:finance.secureOffer')}
476
487
  </Button>
477
- <Button variant="outlined" size="lg" icon="phone" style={{ flex: 1 }} onClick={onCall}>
478
- {t('autobahn:finance.callCta')}
479
- </Button>
480
- </div>
481
-
482
- {/* auto.de-ID (left) · Finanzierungs-/Leasingdetails (right) — same row desktop shows,
483
- restored here per the mockup (round 1 had dropped it in favor of Persönliche Beratung
484
- alone; the mockup wants both rows, not one replacing the other). */}
485
- {(autoDeId || (!isCash && onShowDetails)) && (
486
- <div className="fin-info">
487
- {autoDeId && <span className="fin-id">auto.de-ID {autoDeId}</span>}
488
- {!isCash && onShowDetails && (
489
- <DetailsLink
490
- label={mode === 'leasing' ? t('autobahn:finance.leasingDetails') : t('autobahn:finance.financingDetails')}
491
- onClick={onShowDetails}
492
- />
488
+ <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 }}>
489
+ <Button variant="outlined" size="lg" icon="phone" block onClick={onCall}>
490
+ {t('autobahn:finance.callCta')}
491
+ </Button>
492
+ {autoDeId && (
493
+ <span
494
+ className="num"
495
+ style={{ fontSize: 10, fontWeight: 700, color: 'var(--md-primary)', textAlign: 'center', whiteSpace: 'nowrap' }}
496
+ >
497
+ auto.de ID: {autoDeId}
498
+ </span>
493
499
  )}
494
500
  </div>
495
- )}
501
+ </div>
496
502
 
497
- {/* Persönliche Beratung (left) · Finanzierungs-/Leasingdetails or hint (right) */}
503
+ {/* Persönliche Beratung (left) · divider · Finanzierungs-/Leasingdetails or hint (right) */}
498
504
  <div className="fin-info">
499
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
505
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flex: 1 }}>
500
506
  <Icon name="user" size={18} color="var(--md-on-surface-variant)" style={{ flex: 'none' }} />
501
507
  <div style={{ minWidth: 0 }}>
502
508
  <div className="md-label-sm" style={{ fontWeight: 700, color: 'var(--md-on-surface)' }}>
@@ -505,9 +511,30 @@ const FinanceCalculator: React.FunctionComponent<FinanceCalculatorProps> = ({
505
511
  <div className="md-label-sm">{t('autobahn:finance.personalConsultationSub')}</div>
506
512
  </div>
507
513
  </div>
508
- {/* non-cash: the Finanzierungsdetails link already sits on the auto.de-ID row
509
- above — showing it here too was a duplicate (round-3 regression). */}
510
- {isCash && (
514
+ <span style={{ width: 1, alignSelf: 'stretch', background: 'var(--md-outline-variant)', flex: 'none' }} />
515
+ {!isCash && onShowDetails ? (
516
+ <button
517
+ type="button"
518
+ onClick={onShowDetails}
519
+ style={{
520
+ display: 'flex',
521
+ alignItems: 'center',
522
+ gap: 8,
523
+ background: 'none',
524
+ border: 0,
525
+ padding: 0,
526
+ cursor: 'pointer',
527
+ font: 'inherit',
528
+ color: 'var(--md-on-surface)',
529
+ textAlign: 'left',
530
+ }}
531
+ >
532
+ <Icon name="info" size={18} color="var(--md-on-surface-variant)" style={{ flex: 'none' }} />
533
+ <span className="md-label-sm" style={{ maxWidth: 82, lineHeight: 1.5 }}>
534
+ {mode === 'leasing' ? t('autobahn:finance.leasingDetails') : t('autobahn:finance.financingDetails')}
535
+ </span>
536
+ </button>
537
+ ) : (
511
538
  <div style={{ display: 'flex', alignItems: 'center', gap: 6, color: 'var(--md-on-surface-variant)' }}>
512
539
  <Icon name="info" size={15} />
513
540
  <span className="md-label-sm">{t('autobahn:finance.financingLeasingPossible')}</span>
@@ -1278,7 +1278,8 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
1278
1278
  [
1279
1279
  ['fin', 'autobahn:finance.mode.financing'],
1280
1280
  ['leasing', 'autobahn:finance.mode.leasing'],
1281
- ['cash', 'autobahn:finance.mode.cash'],
1281
+ // this sheet is mobile-only, so the cash tab is "Kaufen" here (desktop card says "Barzahlung")
1282
+ ['cash', 'autobahn:card.price.buy'],
1282
1283
  ] as Array<[FinanceMode, string]>
1283
1284
  ).map(([k, labelKey]) => (
1284
1285
  <button