diff --git a/src/elements/link/block-link-button/block-link-button.visual.spec.ts b/src/elements/link/block-link-button/block-link-button.visual.spec.ts
new file mode 100644
index 0000000000..33e8751b3f
--- /dev/null
+++ b/src/elements/link/block-link-button/block-link-button.visual.spec.ts
@@ -0,0 +1,78 @@
+import { html, nothing } from 'lit';
+
+import {
+ describeEach,
+ describeViewports,
+ visualDiffDefault,
+ visualDiffStandardStates,
+} from '../../core/testing/private.js';
+
+import './block-link-button.js';
+
+describe(`sbb-block-link-button`, () => {
+ const iconState = {
+ iconPlacement: ['start', 'end'],
+ slotted: [false, true],
+ };
+
+ describeViewports({ viewports: ['zero', 'medium'] }, () => {
+ for (const negative of [true, false]) {
+ for (const state of visualDiffStandardStates) {
+ it(
+ state.name,
+ state.with((setup) => {
+ setup.withFixture(
+ html`Travelcards & tickets`,
+ {
+ backgroundColor: negative ? 'var(--sbb-color-charcoal)' : undefined,
+ },
+ );
+ }),
+ );
+ }
+ }
+
+ describeEach(iconState, ({ iconPlacement, slotted }) => {
+ it(
+ visualDiffDefault.name,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(html`
+
+ ${slotted
+ ? html``
+ : nothing}
+ Travelcards & tickets
+
+ `);
+ }),
+ );
+ });
+
+ for (const size of ['xs', 's', 'm']) {
+ it(
+ `size=${size} ${visualDiffDefault.name}`,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(
+ html`Travelcards & tickets`,
+ );
+ }),
+ );
+ }
+
+ it(
+ `width=fixed ${visualDiffDefault.name}`,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(
+ html`
+ A lot of link text to show what happens if there is not enough space.
+ `,
+ );
+ }),
+ );
+ });
+});
diff --git a/src/elements/link/block-link-static/block-link-static.visual.spec.ts b/src/elements/link/block-link-static/block-link-static.visual.spec.ts
new file mode 100644
index 0000000000..cef472c50c
--- /dev/null
+++ b/src/elements/link/block-link-static/block-link-static.visual.spec.ts
@@ -0,0 +1,81 @@
+import { html, nothing } from 'lit';
+
+import {
+ describeEach,
+ describeViewports,
+ visualDiffActive,
+ visualDiffDefault,
+ visualDiffHover,
+} from '../../core/testing/private.js';
+
+import './block-link-static.js';
+
+describe(`sbb-block-link-static`, () => {
+ const iconState = {
+ iconPlacement: ['start', 'end'],
+ slotted: [false, true],
+ };
+
+ describeViewports({ viewports: ['zero', 'medium'] }, () => {
+ for (const negative of [true, false]) {
+ for (const state of [visualDiffDefault, visualDiffActive, visualDiffHover]) {
+ it(
+ state.name,
+ state.with((setup) => {
+ setup.withFixture(
+ html` Travelcards & tickets`,
+ {
+ backgroundColor: negative ? 'var(--sbb-color-charcoal)' : undefined,
+ },
+ );
+ }),
+ );
+ }
+ }
+
+ describeEach(iconState, ({ iconPlacement, slotted }) => {
+ it(
+ visualDiffDefault.name,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(html`
+
+ ${slotted
+ ? html` `
+ : nothing}
+ Travelcards & tickets
+
+ `);
+ }),
+ );
+ });
+
+ for (const size of ['xs', 's', 'm']) {
+ it(
+ `size=${size} ${visualDiffDefault.name}`,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(
+ html` Travelcards & tickets`,
+ );
+ }),
+ );
+ }
+
+ it(
+ `width=fixed ${visualDiffDefault.name}`,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(
+ html`
+ A lot of link text to show what happens if there is not enough space.
+ `,
+ );
+ }),
+ );
+ });
+});
diff --git a/src/elements/link/block-link/block-link.visual.spec.ts b/src/elements/link/block-link/block-link.visual.spec.ts
new file mode 100644
index 0000000000..981f1219cd
--- /dev/null
+++ b/src/elements/link/block-link/block-link.visual.spec.ts
@@ -0,0 +1,79 @@
+import { html, nothing } from 'lit';
+
+import {
+ describeEach,
+ describeViewports,
+ visualDiffDefault,
+ visualDiffStandardStates,
+} from '../../core/testing/private.js';
+
+import './block-link.js';
+
+describe(`sbb-block-link`, () => {
+ const iconState = {
+ iconPlacement: ['start', 'end'],
+ slotted: [false, true],
+ };
+
+ describeViewports({ viewports: ['zero', 'medium'] }, () => {
+ for (const negative of [true, false]) {
+ for (const state of visualDiffStandardStates) {
+ it(
+ state.name,
+ state.with((setup) => {
+ setup.withFixture(
+ html`Travelcards & tickets`,
+ {
+ backgroundColor: negative ? 'var(--sbb-color-charcoal)' : undefined,
+ },
+ );
+ }),
+ );
+ }
+ }
+
+ describeEach(iconState, ({ iconPlacement, slotted }) => {
+ it(
+ visualDiffDefault.name,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(html`
+
+ ${slotted
+ ? html``
+ : nothing}
+ Travelcards & tickets
+
+ `);
+ }),
+ );
+ });
+
+ for (const size of ['xs', 's', 'm']) {
+ it(
+ `size=${size} ${visualDiffDefault.name}`,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(
+ html`Travelcards & tickets`,
+ );
+ }),
+ );
+ }
+
+ it(
+ `width=fixed ${visualDiffDefault.name}`,
+ visualDiffDefault.with((setup) => {
+ setup.withFixture(
+ html`
+ A lot of link text to show what happens if there is not enough space.
+ `,
+ );
+ }),
+ );
+ });
+});