Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sbb-teaser-product): initial implementation #2976

Merged
merged 29 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1b23858
feat(sbb-teaser-product): initial implementation
TomMenga Jul 31, 2024
096bead
fix(sbb-teaser-product): fix import
TomMenga Jul 31, 2024
ad12534
fix(sbb-teaser-product): fix import
TomMenga Jul 31, 2024
974744f
Merge branch 'refs/heads/main' into feat/teaser-product
TomMenga Jul 31, 2024
6f9e7ed
feat(sbb-teaser-product): add base class and implement static variant
TomMenga Aug 6, 2024
85258cf
build(sbb-teaser-product): fix build error
TomMenga Aug 6, 2024
7967e71
test(sbb-teaser-product-static): fix test
TomMenga Aug 6, 2024
804448a
Merge remote-tracking branch 'origin/main' into feat/teaser-product
DavideMininni-Fincons Aug 8, 2024
4d621ad
fix: review (pt. 1)
DavideMininni-Fincons Aug 8, 2024
26d5fb1
Merge remote-tracking branch 'origin/main' into feat/teaser-product
DavideMininni-Fincons Aug 13, 2024
7691990
feat: support slotted img
DavideMininni-Fincons Aug 13, 2024
713d019
feat: support slotted img for static variant, replace p tags with span
DavideMininni-Fincons Aug 13, 2024
d8f6a88
feat: add helper class
DavideMininni-Fincons Aug 13, 2024
29ab000
chore: minor fixes
DavideMininni-Fincons Aug 14, 2024
7d075e4
Merge remote-tracking branch 'origin/main' into feat/teaser-product
DavideMininni-Fincons Aug 14, 2024
a5d45a7
fix: review Jeri
jeripeierSBB Aug 14, 2024
276b17e
fix: snapshots
jeripeierSBB Aug 14, 2024
7242191
fix: fix colors
jeripeierSBB Aug 14, 2024
25f7207
fix: hover, visual tests, snap files
DavideMininni-Fincons Aug 14, 2024
62e8d2c
fix: fix hover transition and reduce visual snapshots
jeripeierSBB Aug 14, 2024
79c212b
feat: high contrast
DavideMininni-Fincons Aug 14, 2024
2ad88e7
refactor: grow with content
jeripeierSBB Aug 14, 2024
0ef40ad
fix: mini review
jeripeierSBB Aug 14, 2024
059ce33
Merge branch 'refs/heads/main' into feat/teaser-product
jeripeierSBB Aug 15, 2024
92cd07a
fix: ux review
jeripeierSBB Aug 15, 2024
f2deff8
fix: ux review
jeripeierSBB Aug 15, 2024
8302652
Merge branch 'refs/heads/main' into feat/teaser-product
jeripeierSBB Aug 15, 2024
e3d9987
Merge branch 'refs/heads/main' into feat/teaser-product
jeripeierSBB Aug 15, 2024
6dfe0ab
fix: ux review
jeripeierSBB Aug 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/elements/teaser-product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './teaser-product/teaser-product.js';
export * from './teaser-product/teaser-product-static.js';
export * from './teaser-product/common.js';
3 changes: 3 additions & 0 deletions src/elements/teaser-product/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './common/teaser-product-common.js';

export { default as teaserProductCommonStyle } from './common/teaser-product-common.scss?lit&inline';
157 changes: 157 additions & 0 deletions src/elements/teaser-product/common/teaser-product-common.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
@use '../../core/styles/index' as sbb;

// Box-sizing rules contained in typography are not traversing Shadow DOM boundaries. We need to include box-sizing mixin in every component.
@include sbb.box-sizing;

:host {
display: block;

--sbb-teaser-product-background-color: var(--sbb-color-cloud);
--sbb-teaser-product-background-gradient-direction: to right;
--sbb-teaser-product-background: var(--sbb-teaser-product-background-color);
--sbb-teaser-product-border-radius: var(--sbb-border-radius-4x);
--sbb-teaser-product-content-color: var(--sbb-color-iron);
--sbb-teaser-product-footer-color: var(--sbb-color-anthracite);
--sbb-teaser-product-container-padding-block: var(--sbb-spacing-responsive-l);
--sbb-teaser-product-min-height: #{sbb.px-to-rem-build(600)};
--sbb-teaser-product-background-gradient-start: 25%;
--sbb-teaser-product-background-gradient-end: 75%;

@include sbb.mq($from: large) {
--sbb-teaser-product-background: linear-gradient(
var(--sbb-teaser-product-background-gradient-direction),
var(--sbb-teaser-product-background-color) var(--sbb-teaser-product-background-gradient-start),
transparent var(--sbb-teaser-product-background-gradient-end)
);
}
}

:host([negative]) {
--sbb-teaser-product-background-color: var(--sbb-color-midnight);
--sbb-teaser-product-content-color: var(--sbb-color-cloud);
--sbb-teaser-product-footer-color: var(--sbb-color-cloud);
--sbb-focus-outline-color: var(--sbb-focus-outline-color-dark);
--sbb-title-text-color-normal-override: var(--sbb-color-milk);
}

:host([image-alignment='before']) {
--sbb-teaser-product-background-gradient-direction: to left;
}

.sbb-teaser-product__image-container {
display: block;
overflow: hidden;

// We have to remove the image bottom border-radius when stacked
border-radius: var(--sbb-teaser-product-border-radius) var(--sbb-teaser-product-border-radius) 0 0;

@include sbb.mq($from: large) {
position: absolute;
inset: 0;
border-radius: var(--sbb-teaser-product-border-radius);
}
}

::slotted(img) {
display: flex;
width: 100%;
height: 100%;
object-fit: cover;
aspect-ratio: 16 / 9;
}

// Reset sbb-image border radius in order to control it from teaser product.
::slotted(sbb-image) {
--sbb-image-border-radius: 0;

height: 100%;
}

::slotted(p.sbb-teaser-product--spacing) {
margin: 0;
}

::slotted(sbb-title.sbb-teaser-product--spacing) {
--sbb-title-margin-block-start: 0;
}

::slotted(:is(sbb-action-group, [data-action]).sbb-teaser-product--spacing) {
margin-block-start: var(--sbb-spacing-responsive-xxs);
}

.sbb-action-base {
display: block;
position: relative;
text-decoration: none;

@include sbb.if-forced-colors {
// Apply a visual border for forced color mode
&::after {
content: '';
position: absolute;
display: block;
inset: 0;
pointer-events: none;
border: var(--sbb-border-width-2x) solid CanvasText;
border-radius: var(--sbb-teaser-product-border-radius);
}
}
}

.sbb-teaser-product__container {
display: block;
background: var(--sbb-teaser-product-background);
border-radius: 0 0 var(--sbb-teaser-product-border-radius) var(--sbb-teaser-product-border-radius);
padding: var(--sbb-spacing-responsive-s);

@include sbb.mq($from: large) {
display: grid;
grid:
'content .' 1fr
'footnote .' auto / 1fr 1fr;
column-gap: var(--sbb-spacing-responsive-xxl);
background: var(--sbb-teaser-product-background);
border-radius: var(--sbb-teaser-product-border-radius);
padding-block: var(--sbb-teaser-product-container-padding-block) 0;
padding-inline: var(--sbb-spacing-responsive-xl);
position: relative;

:host([image-alignment='before']) & {
grid-template-areas:
'. content'
'. footnote';
}
}
}

.sbb-teaser-product__content {
grid-area: content;
align-self: center;
margin: 0;
color: var(--sbb-teaser-product-content-color);

@include sbb.mq($from: large) {
align-content: center;
min-height: calc(
var(--sbb-teaser-product-min-height) - 2 * var(--sbb-teaser-product-container-padding-block)
);
}
}

.sbb-teaser-product__footnote {
grid-area: footnote;
display: block;
padding-block-start: var(--sbb-spacing-responsive-s);
color: var(--sbb-teaser-product-footer-color);

@include sbb.text-xxs--regular;

:host(:not([data-slot-names~='footnote'])) & {
padding-block-start: 0;
}

@include sbb.mq($from: large) {
min-height: var(--sbb-teaser-product-container-padding-block);
padding-block: var(--sbb-spacing-responsive-xs);
}
}
50 changes: 50 additions & 0 deletions src/elements/teaser-product/common/teaser-product-common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { html, type TemplateResult } from 'lit';
import { property } from 'lit/decorators.js';

import type { SbbActionBaseElement } from '../../core/base-elements.js';
import { slotState } from '../../core/decorators.js';
import {
SbbNegativeMixin,
type SbbNegativeMixinType,
type AbstractConstructor,
} from '../../core/mixins.js';

export declare class SbbTeaserProductCommonElementMixinType extends SbbNegativeMixinType {
public imageAlignment?: 'after' | 'before';
}

// eslint-disable-next-line @typescript-eslint/naming-convention
export const SbbTeaserProductCommonElementMixin = <
T extends AbstractConstructor<SbbActionBaseElement>,
>(
superClass: T,
): AbstractConstructor<SbbTeaserProductCommonElementMixinType> & T => {
@slotState()
abstract class SbbTeaserProductCommonElement
extends SbbNegativeMixin(superClass)
implements SbbTeaserProductCommonElementMixinType
{
/**
* Whether the fully visible part of the image is aligned 'before' or 'after' the content.
* Only relevant starting from large breakpoint.
*/
@property({ attribute: 'image-alignment', reflect: true })
public imageAlignment: 'after' | 'before' = 'after';

protected override renderTemplate(): TemplateResult {
return html`
<span class="sbb-teaser-product__image-container"><slot name="image"></slot></span>
<span class="sbb-teaser-product__container">
<span class="sbb-teaser-product__content">
<slot></slot>
</span>
<span class="sbb-teaser-product__footnote">
<slot name="footnote"></slot>
</span>
</span>
`;
}
}
return SbbTeaserProductCommonElement as AbstractConstructor<SbbTeaserProductCommonElementMixinType> &
T;
};
1 change: 1 addition & 0 deletions src/elements/teaser-product/teaser-product-static.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './teaser-product-static/teaser-product-static.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* @web/test-runner snapshot v1 */
export const snapshots = {};

snapshots["sbb-teaser-product-static renders DOM"] =
`<sbb-teaser-product-static
data-action=""
data-slot-names="footnote image unnamed"
dir="ltr"
image-alignment="after"
>
<sbb-image
aspect-ratio="16-9"
border-radius="default"
image-src="http://localhost:8000/src/elements/core/testing/assets/placeholder-image.png"
slot="image"
>
</sbb-image>
<p class="sbb-teaser-product--spacing">
Content
</p>
<p
class="sbb-teaser-product--spacing"
slot="footnote"
>
Footnote
</p>
</sbb-teaser-product-static>
`;
/* end snapshot sbb-teaser-product-static renders DOM */

snapshots["sbb-teaser-product-static renders Shadow DOM"] =
`<span class="sbb-action-base sbb-teaser-product-static">
<span class="sbb-teaser-product__image-container">
<slot name="image">
</slot>
</span>
<span class="sbb-teaser-product__container">
<span class="sbb-teaser-product__content">
<slot>
</slot>
</span>
<span class="sbb-teaser-product__footnote">
<slot name="footnote">
</slot>
</span>
</span>
</span>
`;
/* end snapshot sbb-teaser-product-static renders Shadow DOM */

snapshots["sbb-teaser-product-static renders A11y tree Chrome"] =
`<p>
{
"role": "WebArea",
"name": "",
"children": [
{
"role": "text",
"name": "Content"
},
{
"role": "text",
"name": "Footnote"
}
]
}
</p>
`;
/* end snapshot sbb-teaser-product-static renders A11y tree Chrome */

snapshots["sbb-teaser-product-static renders A11y tree Firefox"] =
`<p>
{
"role": "document",
"name": "",
"children": [
{
"role": "text leaf",
"name": "Content"
},
{
"role": "text leaf",
"name": "Footnote"
}
]
}
</p>
`;
/* end snapshot sbb-teaser-product-static renders A11y tree Firefox */

87 changes: 87 additions & 0 deletions src/elements/teaser-product/teaser-product-static/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
The `sbb-teaser-product-static` is a component that can display a text and a footnote,
combined with an image as background, to tease a product.
It should be used if there is more than one interactive action,
otherwise, see [sbb-teaser-product](/docs/elements-sbb-teaser-sbb-teaser-product--docs).

```html
<sbb-teaser-product-static>
<sbb-image slot="image" image-src="..."></sbb-image>

<p class="sbb-teaser-product--spacing">Content ...</p>

<p slot="footnote" class="sbb-teaser-product--spacing">...</p>
</sbb-teaser-product-static>
```

## Slots

Use the `image` slot to pass a `sbb-image` or an `img` that will be used as a background,
and use the optional `footnote` slot to add a text anchored to the bottom-end of the component.

The default slot is reserved for the main content: it could be a simple text or a text combined with more elements,
like a `sbb-title` or some interactive elements, like buttons or links within the `sbb-action-group` component.

```html
<sbb-teaser-product-static>
<sbb-image slot="image" image-src="..."></sbb-image>
<p class="sbb-teaser-product--spacing">Content ...</p>
</sbb-teaser-product-static>
```

If paragraphs, title and/or button are used, consumers can apply the helper class `sbb-teaser-product--spacing`
to display the components with the correct spacings.

```html
<sbb-teaser-product-static>
<sbb-image slot="image" image-src="..."></sbb-image>
<sbb-title level="3" class="sbb-teaser-product--spacing">
Benefit from up to 70% discount
</sbb-title>
<p class="sbb-teaser-product--spacing">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent pretium felis sit amet felis
viverra lacinia. Donec et enim mi. Aliquam erat volutpat. Proin ut odio tellus.
</p>
<sbb-action-group class="sbb-teaser-product--spacing">
<sbb-button>Label</sbb-button>
<sbb-secondary-button>Label</sbb-secondary-button>
</sbb-action-group>
</sbb-teaser-product-static>
```

## Style

Use the `image-alignment` attribute to anchor the content `after` (on the left) or `before` (on the right).

```html
<sbb-teaser-product-static image-alignment="before"> ... </sbb-teaser-product-static>
```

Add the `negative` attribute to enable the negative variant.

```html
<sbb-teaser-product-static negative> ... </sbb-teaser-product-static>
```

<!-- Auto Generated Below -->

## Properties

| Name | Attribute | Privacy | Type | Default | Description |
| ---------------- | ----------------- | ------- | --------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `imageAlignment` | `image-alignment` | public | `'after' \| 'before'` | `'after'` | Whether the fully visible part of the image is aligned 'before' or 'after' the content. Only relevant starting from large breakpoint. |
| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. |

## CSS Properties

| Name | Default | Description |
| ------------------------------------------------ | ------- | -------------------------------------------------------------------- |
| `--sbb-teaser-product-background-gradient-end` | `75%` | At which percentage the background should be fully transparent. |
| `--sbb-teaser-product-background-gradient-start` | `25%` | At which percentage the background should start getting transparent. |

## Slots

| Name | Description |
| ---------- | ------------------------------------------------------------------- |
| | Use this slot to provide the main content. |
| `footnote` | Use this slot to provide a footnote. |
| `image` | Use this slot to provide an image or a `sbb-image` as a background. |
Loading
Loading