set up basic scroller

This commit is contained in:
MinamiFunakoshiTR 2025-04-22 10:52:56 -04:00
parent 99f20c71f6
commit daeae8653a
Failed to extract signature
8 changed files with 285 additions and 695 deletions

View file

@ -1,42 +0,0 @@
<script lang="ts">
import type { ScrollerStep } from '../@types/global';
interface Props {
index: number;
steps: ScrollerStep[];
preload?: number;
stackBackground?: boolean;
}
let { index, steps, preload = 1, stackBackground = true }: Props = $props();
</script>
{#each steps as step, i}
<!-- Load the step(s) before and after the active one, only -->
<!-- Unless stackBackground is true. If so, keep all steps before the current one loaded. -->
{#if preload === 0 || (i >= (stackBackground ? 0 : index - preload) && i <= index + preload)}
<div
class="step-background step-{i + 1} w-full absolute"
class:visible={stackBackground ? i <= index : i === index}
class:invisible={stackBackground ? i > index : i !== index}
>
<step.background {...step.backgroundProps || {}}></step.background>
</div>
{/if}
{/each}
<style lang="scss">
.step-background {
opacity: 0;
will-change: opacity;
transition: 0.35s opacity ease;
&.visible {
opacity: 1;
}
&.invisible {
opacity: 0;
}
}
</style>

View file

@ -1,18 +0,0 @@
<script lang="ts">
import type { ContainerWidth, ScrollerStep } from '../../@types/global';
import Block from '../../Block/Block.svelte';
interface Props {
step: ScrollerStep;
backgroundWidth: ContainerWidth;
index: number;
}
let { step, backgroundWidth, index }: Props = $props();
</script>
<Block width={backgroundWidth} class="background-container step-{index + 1}">
<div class="embedded-background step-{index + 1}" aria-hidden="true">
<step.background {...step.backgroundProps || {}}></step.background>
</div>
</Block>

View file

@ -1,48 +0,0 @@
<script lang="ts">
import type { ScrollerStep } from '../../@types/global';
import Block from '../../Block/Block.svelte';
import { Markdown } from '@reuters-graphics/svelte-markdown';
interface Props {
step: ScrollerStep;
index: number;
}
let { step, index }: Props = $props();
</script>
{#if step.foreground === '' || !step.foreground}
<!-- Empty foreground -->
<div class="empty-step-foreground step-{index + 1}"></div>
{#if typeof step.altText === 'string'}
<div class="background-alt-text visually-hidden">
<Markdown source={step.altText} />
</div>
{/if}
{:else if typeof step.foreground === 'string'}
<Block class="body-text step-{index + 1}">
<div class="embedded-foreground step-{index + 1}">
<Markdown source={step.foreground} />
</div>
{#if typeof step.altText === 'string'}
<div class="background-alt-text visually-hidden">
<Markdown source={step.altText} />
</div>
{/if}
</Block>
{:else}
<div class="embedded-foreground step-{index + 1}">
<step.foreground {...step.foregroundProps || {}}></step.foreground>
</div>
{/if}
<style lang="scss">
div.embedded-foreground {
:global(p:last-child) {
margin-bottom: 0;
}
}
</style>

View file

@ -1,31 +0,0 @@
<script lang="ts">
import type { ContainerWidth, ScrollerStep } from '../../@types/global';
type EmbeddedLayout = 'fb' | 'bf';
import Background from './Background.svelte';
import Foreground from './Foreground.svelte';
interface Props {
steps: ScrollerStep[];
embeddedLayout?: EmbeddedLayout;
backgroundWidth?: ContainerWidth;
}
let {
steps,
embeddedLayout = 'fb',
backgroundWidth = 'fluid',
}: Props = $props();
</script>
{#each steps as step, index}
<!-- Background first -->
{#if embeddedLayout === 'bf'}
<Background {step} {index} {backgroundWidth} />
<Foreground {step} {index} />
<!-- Foreground first, default -->
{:else}
<Foreground {step} {index} />
<Background {step} {index} {backgroundWidth} />
{/if}
{/each}

View file

@ -1,64 +0,0 @@
<script lang="ts">
import type { ScrollerStep } from '../@types/global';
import { Markdown } from '@reuters-graphics/svelte-markdown';
interface Props {
steps: ScrollerStep[];
}
let { steps }: Props = $props();
</script>
{#each steps as step, i}
<div
class="step-foreground-container step-{i +
1} mb-20 h-screen flex items-center justify-center"
>
{#if step.foreground === '' || !step.foreground}
<!-- Empty foreground -->
<div class="empty-step-foreground"></div>
{#if typeof step.altText === 'string'}
<div class="background-alt-text visually-hidden">
<Markdown source={step.altText} />
</div>
{/if}
{:else}
<div class="step-foreground w-full">
{#if typeof step.foreground === 'string'}
<Markdown source={step.foreground} />
{:else}
<step.foreground {...step.foregroundProps || {}}></step.foreground>
{/if}
</div>
{#if typeof step.altText === 'string'}
<div class="background-alt-text visually-hidden">
<Markdown source={step.altText} />
</div>
{/if}
{/if}
</div>
{/each}
<style lang="scss">
@use './../../scss/mixins' as mixins;
div.step-foreground-container {
width: initial;
max-width: initial;
.step-foreground {
max-width: calc(mixins.$column-width-normal * 0.9);
border-radius: 0.25rem;
@include mixins.fpy-5;
@include mixins.fpx-4;
background: rgba(255, 255, 255, 0.9);
:global(p:last-child) {
margin-bottom: 0;
}
:global(*:first-child) {
margin-top: 0;
}
}
}
</style>

View file

@ -1,270 +0,0 @@
<!-- This is a Svelte 5 version of [svelte-scroller](https://github.com/sveltejs/svelte-scroller) -->
<script module lang="ts">
const handlers: Array<() => void> = [];
interface ManagerParams {
outer: Element;
update: () => void;
}
let manager: {
add: (params: ManagerParams) => void;
remove: (params: ManagerParams) => void;
};
if (typeof window !== 'undefined') {
const run_all = () => handlers.forEach((fn) => fn());
window.addEventListener('scroll', run_all);
window.addEventListener('resize', run_all);
}
if (typeof IntersectionObserver !== 'undefined') {
const map = new Map();
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const update = map.get(entry.target);
const index = handlers.indexOf(update);
if (entry.isIntersecting) {
if (index === -1) handlers.push(update);
} else {
update();
if (index !== -1) handlers.splice(index, 1);
}
});
},
{
rootMargin: '400px 0px', // TODO why 400?
}
);
manager = {
add: ({ outer, update }) => {
const { top, bottom } = outer.getBoundingClientRect();
if (top < window.innerHeight && bottom > 0) handlers.push(update);
map.set(outer, update);
observer.observe(outer);
},
remove: ({ outer, update }) => {
const index = handlers.indexOf(update);
if (index !== -1) handlers.splice(index, 1);
map.delete(outer);
observer.unobserve(outer);
},
};
} else {
manager = {
add: ({ update }) => {
handlers.push(update);
},
remove: ({ update }) => {
const index = handlers.indexOf(update);
if (index !== -1) handlers.splice(index, 1);
},
};
}
</script>
<script lang="ts">
import { onMount } from 'svelte';
import { type Snippet } from 'svelte';
interface Props {
/** Config */
/** The vertical position that the top of the foreground must scroll past before the background becomes fixed, as a proportion of window height. **Value between 0 and 1.** */
top?: number;
/** The inverse of top — once the bottom of the foreground passes this point, the background becomes unfixed. **Value between 0 and 1.** */
bottom?: number;
/** Once a section crosses this point, it becomes 'active'. **Value between 0 and 1.** */
threshold?: number;
/** A CSS selector that describes the individual sections of your foreground. */
query?: string;
/** If `true`, the background will scroll such that the bottom edge reaches the bottom at the same time as the foreground. This effect can be unpleasant for people with high motion sensitivity, so use it advisedly. */
parallax?: boolean;
/** The background snippet. */
backgroundSnippet: Snippet;
/** The foreground snippet. */
foregroundSnippet: Snippet;
/** Bindings */
/** The currently active section. **Bindable** */
index?: number;
/** How far the section has scrolled past the threshold, as a value between 0 and 1. **Bindable**. */
offset?: number;
/** How far the foreground has travelled, where 0 is the top of the foreground crossing top, and 1 is the bottom crossing bottom. **Bindable**. */
progress?: number;
/** Number of sections */
count?: number;
/** Whether the foreground is visible */
visible?: boolean;
}
let {
// Bindings
index = $bindable(0),
count = $bindable(0),
offset = $bindable(0),
progress = $bindable(0),
visible = $bindable(false),
// Config
top = 0,
bottom = 1,
threshold = 0.5,
query = 'section',
parallax = false,
backgroundSnippet,
foregroundSnippet,
}: Props = $props();
let outer: HTMLElement;
let foreground: HTMLElement;
let background: HTMLElement;
let left;
// Target compiler option to es6 or higher for NodeListOf<T> to be iterable.
let sections: ReturnType<typeof document.querySelectorAll> | undefined =
$state();
let wh = $state(0);
let fixed = $state(false);
let offset_top = 0;
let width = 1;
let top_px = $derived(Math.round(top * wh));
let bottom_px = $derived(Math.round(bottom * wh));
let threshold_px = $derived(Math.round(threshold * wh));
let style = $derived(`
position: ${fixed ? 'fixed' : 'absolute'};
transform: translate(0, ${offset_top}px);
`);
let widthStyle = $derived(fixed ? `width:${width}px;` : '');
onMount(() => {
sections = foreground.querySelectorAll(query);
count = sections.length;
update();
const scroller = { outer, update };
manager.add(scroller);
return () => manager.remove(scroller);
});
function update() {
if (!foreground) return;
// re-measure outer container
const bcr = outer.getBoundingClientRect();
left = bcr.left;
width = bcr.right - left;
// determine fix state
const fg = foreground.getBoundingClientRect();
const bg = background.getBoundingClientRect();
visible = fg.top < wh && fg.bottom > 0;
const foreground_height = fg.bottom - fg.top;
const background_height = bg.bottom - bg.top;
const available_space = bottom_px - top_px;
progress = (top_px - fg.top) / (foreground_height - available_space);
if (progress <= 0) {
offset_top = 0;
fixed = false;
} else if (progress >= 1) {
offset_top =
parallax ?
foreground_height - background_height
: foreground_height - available_space;
fixed = false;
} else {
offset_top =
parallax ?
Math.round(top_px - progress * (background_height - available_space))
: top_px;
fixed = true;
}
for (let i = 0; i < sections!.length; i++) {
const section = sections![i];
const { top } = section.getBoundingClientRect();
const next = sections![i + 1];
const bottom = next ? next.getBoundingClientRect().top : fg.bottom;
offset = (threshold_px - top) / (bottom - top);
if (bottom >= threshold_px) {
index = i;
break;
}
}
}
</script>
<svelte:window bind:innerHeight={wh} />
<svelte-scroller-outer bind:this={outer}>
<svelte-scroller-background-container
class="background-container"
style="{style}{widthStyle}"
>
<svelte-scroller-background bind:this={background}>
{@render backgroundSnippet()}
</svelte-scroller-background>
</svelte-scroller-background-container>
<svelte-scroller-foreground bind:this={foreground}>
{@render foregroundSnippet()}
</svelte-scroller-foreground>
</svelte-scroller-outer>
<style lang="scss">
svelte-scroller-outer {
display: block;
position: relative;
}
svelte-scroller-background {
display: block;
position: relative;
width: 100%;
}
svelte-scroller-foreground {
display: block;
position: relative;
z-index: 2;
}
svelte-scroller-foreground::after {
content: ' ';
display: block;
clear: both;
}
svelte-scroller-background-container {
display: block;
position: absolute;
width: 100%;
max-width: 100%;
pointer-events: none;
z-index: 1;
top: 0;
/* in theory this helps prevent jumping */
will-change: transform;
/* -webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); */
}
</style>

View file

@ -94,36 +94,29 @@
} as const;
</script>
<Story
name="Demo"
args={{
steps: [
{
background: MyBackground,
backgroundProps: { colour: 'red' },
foreground: '#### Step 1\n\nLorem ipsum red',
altText: 'Red background',
},
{
background: MyBackground,
backgroundProps: { colour: 'blue' },
foreground: '#### Step 2\n\nLorem ipsum blue',
altText: 'Blue background',
},
{
background: MyBackground,
backgroundProps: { colour: 'green' },
foreground: '#### Step 3\n\nLorem ipsum green',
altText: 'Green background',
},
],
foregroundPosition: 'middle',
backgroundWidth: 'fluid',
}}
/>
<Story name="Demo">
<SvelteScroller>
{#snippet backgroundSnippet(index)}
<div class="custom-background">
<p>
This is the background content. It will stay fixed in place while the
foreground scrolls over the top.
</p>
<p>Section {index + 1} is currently active.</p>
</div>
{/snippet}
{#snippet foregroundSnippet(index)}
<div class="foreground">Index {index}: This is the first section.</div>
<div class="foreground">Index {index}: This is the second section.</div>
<div class="foreground">Index {index}: This is the third section.</div>
{/snippet}
</SvelteScroller>
</Story>
<!--
<Story name="ArchieML and ai2svelte" exportName="ArchieML">
<Scroller
<SvelteScroller
id={docBlock.id}
foregroundPosition={docBlock.foregroundPosition}
stackBackground={docBlock.stackBackground === 'true'}
@ -133,34 +126,9 @@
altText: step.altText,
}))}
/>
</Story>
</Story> -->
<Story
name="Custom foreground"
exportName="CustomForeground"
args={{
steps: [
{
background: MyBackground,
backgroundProps: { colour: 'red' },
foreground: MyInteractiveForeground,
},
{
background: MyBackground,
backgroundProps: { colour: 'blue' },
foreground: '#### Step 2\n\nLorem ipsum blue',
},
{
background: MyBackground,
backgroundProps: { colour: 'green' },
foreground: MyInteractiveForeground,
foregroundProps: { count: 100 },
},
],
}}
/>
<Story
<!-- <Story
name="Custom foreground with ArchiemL"
exportName="CustomforegroundArchieML"
>
@ -174,4 +142,18 @@
altText: step.altText,
}))}
/>
</Story>
</Story> -->
<style lang="scss">
.custom-background {
padding: 20px;
border-radius: 5px;
height: 100vh;
}
.foreground {
height: 80vh;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 1em;
margin: 0 0 2em 0;
}
</style>

View file

@ -1,189 +1,270 @@
<!-- @component `Scroller` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-graphics-scroller--docs) -->
<script lang="ts">
import ScrollerBase from './ScrollerBase/index.svelte';
import Background from './Background.svelte';
import Foreground from './Foreground.svelte';
import Embedded from './Embedded/index.svelte';
import Block from '../Block/Block.svelte';
<!-- This is a Svelte 5 version of [svelte-scroller](https://github.com/sveltejs/svelte-scroller) -->
<script module lang="ts">
const handlers: Array<() => void> = [];
// Types
import type {
ContainerWidth,
ForegroundPosition,
ScrollerStep,
} from '../@types/global';
interface ManagerParams {
outer: Element;
update: () => void;
}
let manager: {
add: (params: ManagerParams) => void;
remove: (params: ManagerParams) => void;
};
if (typeof window !== 'undefined') {
const run_all = () => handlers.forEach((fn) => fn());
window.addEventListener('scroll', run_all);
window.addEventListener('resize', run_all);
}
if (typeof IntersectionObserver !== 'undefined') {
const map = new Map();
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const update = map.get(entry.target);
const index = handlers.indexOf(update);
if (entry.isIntersecting) {
if (index === -1) handlers.push(update);
} else {
update();
if (index !== -1) handlers.splice(index, 1);
}
});
},
{
rootMargin: '400px 0px', // TODO why 400?
}
);
manager = {
add: ({ outer, update }) => {
const { top, bottom } = outer.getBoundingClientRect();
if (top < window.innerHeight && bottom > 0) handlers.push(update);
map.set(outer, update);
observer.observe(outer);
},
remove: ({ outer, update }) => {
const index = handlers.indexOf(update);
if (index !== -1) handlers.splice(index, 1);
map.delete(outer);
observer.unobserve(outer);
},
};
} else {
manager = {
add: ({ update }) => {
handlers.push(update);
},
remove: ({ update }) => {
const index = handlers.indexOf(update);
if (index !== -1) handlers.splice(index, 1);
},
};
}
</script>
<script lang="ts">
import { onMount } from 'svelte';
import { type Snippet } from 'svelte';
interface Props {
/**
* An array of step objects that define the steps in your scroller.
*
* Each step object in the array can have:
*
* - `background` A background component. **REQUIRED**
* - `backgroundProps` Optional props for background component.
* - `foreground` A component or markdown-formatted string. **REQUIRED**
* - `foregroundProps` Optional props for foreground component.
* - `altText` Optional alt text for the background, read aloud after the foreground text. You can add it to each step or just to the first step to describe the entire scroller graphic. **RECOMMENDED**
*
*/
steps: ScrollerStep[];
/** Width of the background */
backgroundWidth?: ContainerWidth;
/** Position of the foreground */
foregroundPosition?: ForegroundPosition;
/**
* Whether previous background steps should stack below the current one.
*
* - `true` _default_ Background graphics from previous steps will remain visible below the active one, allowing you to stack graphics with transparent backgrounds.
* - `false` Only the background graphic from the current step will show and backgrounds from previous steps are hidden.
*/
stackBackground?: boolean;
/**
* How many background steps to load before and after the currently active one, effectively lazy-loading them.
*
* Setting to `0` disables lazy-loading and loads all backgrounds at once.
*/
preload?: number;
/** Setting to `true` will unroll the scroll experience into a flat layout */
embedded?: boolean;
/**
* Layout order when `embedded` is `true`.
*
* - `fb` _default_ Foreground then background
* - `bf` Background then foreground
*
*/
embeddedLayout?: 'fb' | 'bf';
/**
* Threshold prop passed to [svelte-scroller](https://github.com/sveltejs/svelte-scroller#parameters)
*/
threshold?: number;
/**
* Top prop passed to [svelte-scroller](https://github.com/sveltejs/svelte-scroller#parameters)
*/
/** Config */
/** The vertical position that the top of the foreground must scroll past before the background becomes fixed, as a proportion of window height. **Value between 0 and 1.** */
top?: number;
/**
* Bottom prop passed to [svelte-scroller](https://github.com/sveltejs/svelte-scroller#parameters)
*/
/** The inverse of top — once the bottom of the foreground passes this point, the background becomes unfixed. **Value between 0 and 1.** */
bottom?: number;
/**
* Parallax prop passed to [svelte-scroller](https://github.com/sveltejs/svelte-scroller#parameters)
*/
/** Once a section crosses this point, it becomes 'active'. **Value between 0 and 1.** */
threshold?: number;
/** A CSS selector that describes the individual sections of your foreground. */
query?: string;
/** If `true`, the background will scroll such that the bottom edge reaches the bottom at the same time as the foreground. This effect can be unpleasant for people with high motion sensitivity, so use it advisedly. */
parallax?: boolean;
/** ID of the scroller container */
id?: string;
/** Set a class to target with SCSS */
class?: string;
/** The background snippet. */
backgroundSnippet: Snippet;
/** The foreground snippet. */
foregroundSnippet: Snippet;
/** Bindings */
/** The currently active section. **Bindable** */
index?: number;
/** How far the section has scrolled past the threshold, as a value between 0 and 1. **Bindable**. */
offset?: number;
/** How far the foreground has travelled, where 0 is the top of the foreground crossing top, and 1 is the bottom crossing bottom. **Bindable**. */
progress?: number;
/** Number of sections */
count?: number;
/** Whether the foreground is visible */
visible?: boolean;
}
let {
id = '',
steps,
backgroundWidth = 'fluid',
foregroundPosition = 'middle',
stackBackground = true,
preload = 1,
embedded = false,
embeddedLayout = 'fb',
threshold = 0.5,
// Bindings
index = $bindable(0),
count = $bindable(0),
offset = $bindable(0),
progress = $bindable(0),
visible = $bindable(false),
// Config
top = 0,
bottom = 1,
threshold = 0.5,
query = 'section',
parallax = false,
class: cls = '',
backgroundSnippet,
foregroundSnippet,
}: Props = $props();
// Bindable variables passed to ScrollerBase
let index = $state(0);
let offset = $state(0);
let progress = $state(0);
let outer: HTMLElement;
let foreground: HTMLElement;
let background: HTMLElement;
let left;
// Target compiler option to es6 or higher for NodeListOf<T> to be iterable.
let sections: ReturnType<typeof document.querySelectorAll> | undefined =
$state();
let wh = $state(0);
let fixed = $state(false);
let offset_top = 0;
let width = 1;
let top_px = $derived(Math.round(top * wh));
let bottom_px = $derived(Math.round(bottom * wh));
let threshold_px = $derived(Math.round(threshold * wh));
let style = $derived(`
position: ${fixed ? 'fixed' : 'absolute'};
transform: translate(0, ${offset_top}px);
`);
let widthStyle = $derived(fixed ? `width:${width}px;` : '');
onMount(() => {
sections = foreground.querySelectorAll(query);
count = sections.length;
update();
const scroller = { outer, update };
manager.add(scroller);
return () => manager.remove(scroller);
});
function update() {
if (!foreground) return;
// re-measure outer container
const bcr = outer.getBoundingClientRect();
left = bcr.left;
width = bcr.right - left;
// determine fix state
const fg = foreground.getBoundingClientRect();
const bg = background.getBoundingClientRect();
visible = fg.top < wh && fg.bottom > 0;
const foreground_height = fg.bottom - fg.top;
const background_height = bg.bottom - bg.top;
const available_space = bottom_px - top_px;
progress = (top_px - fg.top) / (foreground_height - available_space);
if (progress <= 0) {
offset_top = 0;
fixed = false;
} else if (progress >= 1) {
offset_top =
parallax ?
foreground_height - background_height
: foreground_height - available_space;
fixed = false;
} else {
offset_top =
parallax ?
Math.round(top_px - progress * (background_height - available_space))
: top_px;
fixed = true;
}
for (let i = 0; i < sections!.length; i++) {
const section = sections![i];
const { top } = section.getBoundingClientRect();
const next = sections![i + 1];
const bottom = next ? next.getBoundingClientRect().top : fg.bottom;
offset = (threshold_px - top) / (bottom - top);
if (bottom >= threshold_px) {
index = i;
break;
}
}
}
</script>
{#if !embedded}
<Block width="fluid" class="scroller-container fmy-6 {cls}" {id}>
<ScrollerBase
bind:index
bind:offset
bind:progress
{threshold}
{top}
{bottom}
{parallax}
query="div.step-foreground-container"
>
{#snippet backgroundSnippet()}
<div
class="background min-h-screen relative p-0 flex justify-center"
class:right={foregroundPosition === 'left opposite'}
class:left={foregroundPosition === 'right opposite'}
aria-hidden="true"
>
<div class="scroller-graphic-well w-full">
<Block
width={backgroundWidth}
class="background-container step-{index +
1} my-0 min-h-screen flex justify-center items-center relative"
>
<Background {index} {steps} {preload} {stackBackground} />
</Block>
</div>
</div>
{/snippet}
{#snippet foregroundSnippet()}
<div class="foreground {foregroundPosition} w-full">
<Foreground {steps} />
</div>
{/snippet}
</ScrollerBase>
</Block>
{:else}
<Block width="widest" class="scroller-container embedded" {id}>
<Embedded {steps} {embeddedLayout} {backgroundWidth} />
</Block>
{/if}
<svelte:window bind:innerHeight={wh} />
<svelte-scroller-outer bind:this={outer}>
<svelte-scroller-background-container
class="background-container"
style="{style}{widthStyle}"
>
<svelte-scroller-background bind:this={background}>
{@render backgroundSnippet(index)}
</svelte-scroller-background>
</svelte-scroller-background-container>
<svelte-scroller-foreground bind:this={foreground}>
{@render foregroundSnippet(index)}
</svelte-scroller-foreground>
</svelte-scroller-outer>
<style lang="scss">
div.background {
&.left {
width: 50%;
float: left;
@media (max-width: 1200px) {
justify-content: center;
width: 100%;
float: initial;
}
}
&.right {
width: 50%;
float: right;
@media (max-width: 1200px) {
justify-content: center;
width: 100%;
float: initial;
}
}
div.scroller-graphic-well {
padding: 0 15px;
}
svelte-scroller-outer {
display: block;
position: relative;
}
div.foreground {
&.right {
width: 50%;
float: right;
@media (max-width: 1200px) {
width: 100%;
float: initial;
}
}
svelte-scroller-background {
display: block;
position: relative;
width: 100%;
}
&.left {
width: 50%;
float: left;
@media (max-width: 1200px) {
width: 100%;
float: initial;
}
}
svelte-scroller-foreground {
display: block;
position: relative;
z-index: 2;
}
svelte-scroller-foreground::after {
content: ' ';
display: block;
clear: both;
}
svelte-scroller-background-container {
display: block;
position: absolute;
width: 100%;
max-width: 100%;
pointer-events: none;
z-index: 1;
top: 0;
/* in theory this helps prevent jumping */
will-change: transform;
/* -webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); */
}
</style>