@bytebrand/fe-ui-core-autobahn 1.0.3 → 1.0.4

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
@@ -65,45 +65,45 @@ const CarRow: React.FunctionComponent<CarRowProps> = ({ car, isFavorite, onToggl
65
65
  onClick={() => onOpen(car)}
66
66
  style={{ background: 'var(--md-sc-lowest)' }}
67
67
  >
68
- <div className="listcard-media" onMouseEnter={onMediaEnter} onTouchStart={onMediaEnter}>
69
- <div className="listcard-media-img">
70
- <CardCarousel images={car.gallery} height="100%" fit="contain" />
71
- {/* deal badges — ONE compact line: NEU · −€ · 0 € Anz. · −%. The price RATING
72
- ('Guter Preis') is NOT here — it lives in the price column (PricePaths meter). */}
73
- <div
74
- style={{
75
- position: 'absolute',
76
- top: 10,
77
- left: 10,
78
- right: 10,
79
- zIndex: 3,
80
- display: 'flex',
81
- flexDirection: 'row',
82
- flexWrap: 'nowrap',
83
- alignItems: 'center',
84
- gap: 5,
85
- }}
86
- >
87
- {car.isNew && <span className="neu-badge">NEU</span>}
88
- {car.drop != null ? <DropChip t={t} amount={car.drop} /> : showSave && <SaveChip t={t} amount={car.savings!} />}
89
- <ZeroDownChip t={t} show={car.zeroDown} compact />
90
- <PercentChip pct={car.dropPct} />
91
- </div>
92
- <div style={{ position: 'absolute', bottom: 10, right: 10, zIndex: 3 }}>
93
- <span className="imglabel">
94
- <Icon name="image" size={13} />
95
- {car.photos}
96
- </span>
97
- </div>
98
- <div
99
- onClick={(e) => e.stopPropagation()}
100
- className="show-mobile"
101
- style={{ position: 'absolute', top: 8, right: 8, zIndex: 3 }}
102
- >
103
- <IconButton name="heart" filled active={isFavorite} onClick={onToggleFavorite} label={t('autobahn:card.action.save')} />
104
- </div>
68
+ <div className="listcard-media" style={{ position: 'relative' }} onMouseEnter={onMediaEnter} onTouchStart={onMediaEnter}>
69
+ <CardCarousel images={car.gallery} height="100%" fit="contain" />
70
+ {/* deal badges — ONE compact line: NEU · −€ · 0 € Anz. · −%. The price RATING
71
+ ('Guter Preis') is NOT here — it lives in the price column (PricePaths meter). */}
72
+ <div
73
+ style={{
74
+ position: 'absolute',
75
+ top: 10,
76
+ left: 10,
77
+ right: 10,
78
+ zIndex: 3,
79
+ display: 'flex',
80
+ flexDirection: 'row',
81
+ flexWrap: 'nowrap',
82
+ alignItems: 'center',
83
+ gap: 5,
84
+ }}
85
+ >
86
+ {car.isNew && <span className="neu-badge">NEU</span>}
87
+ {car.drop != null ? <DropChip t={t} amount={car.drop} /> : showSave && <SaveChip t={t} amount={car.savings!} />}
88
+ <ZeroDownChip t={t} show={car.zeroDown} compact />
89
+ <PercentChip pct={car.dropPct} />
90
+ </div>
91
+ <div style={{ position: 'absolute', bottom: 10, right: 10, zIndex: 3 }}>
92
+ <span className="imglabel">
93
+ <Icon name="image" size={13} />
94
+ {car.photos}
95
+ </span>
96
+ </div>
97
+ <div
98
+ onClick={(e) => e.stopPropagation()}
99
+ className="show-mobile"
100
+ style={{ position: 'absolute', top: 8, right: 8, zIndex: 3 }}
101
+ >
102
+ <IconButton name="heart" filled active={isFavorite} onClick={onToggleFavorite} label={t('autobahn:card.action.save')} />
105
103
  </div>
106
- {/* activity/trust pill row — always under the image, never overlaid on it. */}
104
+ {/* activity/trust badges — overlaid on the studio-white floor at the bottom of the photo
105
+ itself (PM ref), not a separate divided strip below it. Opposite corner from the photo
106
+ count so the two never collide. */}
107
107
  <GalleryBadges car={car} t={t} />
108
108
  </div>
109
109
 
@@ -494,34 +494,36 @@ export interface GalleryBadgesProps {
494
494
  }
495
495
 
496
496
  /**
497
- * Trust/activity pill row rendered under the gallery on the SRP list card — mirrors the mobile
498
- * VDP gallery header (availability · views · favorites · Geprüft).
497
+ * Trust/activity badges overlaid on the photo itself, bottom-left (PM reference, 2026-07-10) —
498
+ * flat icon+text, no pill border/background/divider, sitting on the studio-white floor every
499
+ * inventory photo already has (see .cc-media/.listcard-media background in md3.css). Mirrors the
500
+ * mobile VDP gallery header (availability · views · favorites · Geprüft).
499
501
  *
500
- * PM decision (2026-07-10, Viktoriia — "this logic already exists on prod"): the row is all-or-
501
- * nothing. A missing or zero view/favorite count is NOT shown as "0×" — the whole row (including
502
- * the always-on availability/Geprüft pills) is hidden rather than reading as an empty/broken card.
503
- * It only appears once BOTH counts are real and positive.
502
+ * PM decision (Viktoriia — "this logic already exists on prod"): the row is all-or-nothing. A
503
+ * missing or zero view/favorite count is NOT shown as "0×" — the whole row (including the
504
+ * always-on availability/Geprüft items) is hidden rather than reading as an empty/broken card. It
505
+ * only appears once BOTH counts are real and positive.
504
506
  */
505
507
  export const GalleryBadges: React.FunctionComponent<GalleryBadgesProps> = ({ car, t }) => {
506
508
  if (!car.viewsCount || !car.favoritesCount) return null;
507
509
  return (
508
- <div className="gallery-badges">
510
+ <div className="gallery-badges" style={{ position: 'absolute', left: 10, bottom: 10, zIndex: 3 }}>
509
511
  {car.availability && (
510
512
  <span className="gallery-pill">
511
- <Icon name="circle-check" size={16} stroke={2} color="var(--md-good)" />
513
+ <Icon name="circle-check" size={15} stroke={2} color="var(--md-good)" />
512
514
  {car.availability}
513
515
  </span>
514
516
  )}
515
517
  <span className="gallery-pill">
516
- <Icon name="eye" size={16} stroke={1.9} />
518
+ <Icon name="eye" size={15} stroke={1.9} />
517
519
  {t('autobahn:card.activity.viewsCount', { count: car.viewsCount })}
518
520
  </span>
519
521
  <span className="gallery-pill">
520
- <Icon name="heart" size={16} stroke={1.9} />
522
+ <Icon name="heart" size={15} stroke={1.9} />
521
523
  {t('autobahn:card.activity.favoritesCount', { count: car.favoritesCount })}
522
524
  </span>
523
525
  <span className="gallery-pill">
524
- <Icon name="shield-check" size={16} stroke={1.9} />
526
+ <Icon name="shield-check" size={15} stroke={1.9} />
525
527
  {t('autobahn:card.trust.checked')}
526
528
  </span>
527
529
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/fe-ui-core-autobahn",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
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
@@ -1002,17 +1002,13 @@
1002
1002
 
1003
1003
  /* horizontal listing card (SRP): image | info | price */
1004
1004
  .md3 .listcard { display: grid; grid-template-columns: 300px 1fr 240px; align-items: stretch; }
1005
- .md3 .listcard-media { position: relative; min-height: 100%; display: flex; flex-direction: column; }
1006
- /* wraps the carousel + its absolute overlays; flexes to fill whatever height the badge row below
1007
- doesn't need, so the row height stays governed by the grid (like before this row existed). */
1008
- .md3 .listcard-media-img { position: relative; flex: 1 1 auto; min-height: 0; }
1009
- /* activity/trust pill row under the SRP card image (availability · views · favorites · Geprüft).
1010
- Sizing/radius per Figma spec: 30px tall, 11px corner radius (NOT a full pill), 1px border. */
1011
- .md3 .gallery-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 12px;
1012
- border-top: 1px solid var(--md-outline-variant); background: var(--md-sc-lowest); flex: none; }
1013
- .md3 .gallery-pill { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px;
1014
- border-radius: 11px; border: 1px solid var(--md-outline-variant); background: var(--md-sc-lowest);
1015
- color: var(--md-on-surface); font-size: 13px; font-weight: 600; white-space: nowrap; }
1005
+ .md3 .listcard-media { position: relative; min-height: 100%; }
1006
+ /* activity/trust badges overlaid on the photo's studio-white floor, bottom-left (opposite corner
1007
+ from the bottom-right photo-count .imglabel so they never collide) — PM ref, no divider/box,
1008
+ flat icon+text pairs. */
1009
+ .md3 .gallery-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
1010
+ .md3 .gallery-pill { display: inline-flex; align-items: center; gap: 5px; color: var(--md-on-surface-variant);
1011
+ font-size: 13px; font-weight: 600; white-space: nowrap; }
1016
1012
  .md3 .listcard-info { padding: 16px 18px; display: flex; flex-direction: column; gap: 11px; min-width: 0; }
1017
1013
  .md3 .listcard-price { position: relative; padding: 16px 18px; border-left: 1px solid var(--md-outline-variant);
1018
1014
  display: flex; flex-direction: column; justify-content: flex-start; gap: 16px; background: var(--md-sc-low); }
@@ -1298,10 +1294,9 @@
1298
1294
 
1299
1295
  @media (max-width: 600px) {
1300
1296
  .md3 .cards-3, .md3 .cards-2 { grid-template-columns: 1fr; }
1301
- /* listing card stacks: image (4:3) → badges → info → price */
1297
+ /* listing card stacks: image (4:3) → info → price */
1302
1298
  .md3 .listcard { grid-template-columns: 1fr; }
1303
- .md3 .listcard-media { min-height: 0; }
1304
- .md3 .listcard-media-img { aspect-ratio: 4 / 3; flex: none; }
1299
+ .md3 .listcard-media { aspect-ratio: 4 / 3; min-height: 0; }
1305
1300
  .md3 .listcard-price { border-left: 0; border-top: 1px solid var(--md-outline-variant); padding: 14px 16px;
1306
1301
  flex-direction: column; justify-content: flex-start; align-items: stretch; gap: 14px; }
1307
1302
  .md3 .listcard-price .pricepaths { flex: none; min-width: 0; }