makes svelte scroller
42
src/components/SvelteScroller/Background.svelte
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<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>
|
||||
18
src/components/SvelteScroller/Embedded/Background.svelte
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<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>
|
||||
48
src/components/SvelteScroller/Embedded/Foreground.svelte
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<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>
|
||||
31
src/components/SvelteScroller/Embedded/index.svelte
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<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}
|
||||
64
src/components/SvelteScroller/Foreground.svelte
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<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>
|
||||
270
src/components/SvelteScroller/ScrollerBase/index.svelte
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
<!-- 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>
|
||||
279
src/components/SvelteScroller/SvelteScroller.mdx
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
import { Meta } from '@storybook/blocks';
|
||||
|
||||
import * as SvelteScrollerStories from './SvelteScroller.stories.svelte';
|
||||
|
||||
<Meta of={SvelteScrollerStories} />
|
||||
|
||||
# SvelteScroller
|
||||
|
||||
The `SvelteScroller` component creates a basic scrollytelling graphic with layout options.
|
||||
|
||||
> This component is designed to handle most common layouts for scrollytelling. To make something more complex, customise [ScrollerBase](https://github.com/reuters-graphics/graphics-components/blob/main/src/components/Scroller/ScrollerBase/index.svelte), which is a Svelte 5 version of the [svelte-scroller](https://github.com/sveltejs/svelte-scroller).
|
||||
|
||||
[Demo](?path=/story/components-graphics-scroller--demo)
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Scroller } from '@reuters-graphics/graphics-components';
|
||||
|
||||
import MyBackground from './MyBackground.svelte'; // Your own background component
|
||||
|
||||
// Array of step objects that define the steps in your scroller.
|
||||
const steps = [
|
||||
{
|
||||
background: MyBackground,
|
||||
backgroundProps: { colour: 'red' }, // Optional props for your background component
|
||||
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',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<Scroller {steps} foregroundPosition="middle" backgroundWidth="fluid" />
|
||||
```
|
||||
|
||||
## Using with ArchieML and ai2svelte
|
||||
|
||||
[Demo](?path=/story/components-graphics-scroller--archie-ml)
|
||||
|
||||
In your graphics kit project, import your ai2svelte graphics in `App.svelte` and add them to the `aiCharts` object:
|
||||
|
||||
```svelte
|
||||
<!-- App.svelte -->
|
||||
<script>
|
||||
import AiMap1 from './ai2svelte/my-map-1.svelte';
|
||||
import AiMap2 from './ai2svelte/my-map-2.svelte';
|
||||
import AiMap3 from './ai2svelte/my-map-3.svelte';
|
||||
|
||||
import content from '$locales/en/content.json';
|
||||
|
||||
// Graphics kit only
|
||||
import { assets } from '$app/paths'; // 👈 If using in the graphics kit...
|
||||
import { truthy } from '$utils/propValidators'; // 👈 If using in the graphics kit...
|
||||
|
||||
const aiCharts = {
|
||||
AiMap1,
|
||||
AiMap2,
|
||||
AiMap3,
|
||||
// Other charts...
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Then add the following structure to your ArchieML Doc, making sure that the names of your charts in the `aiCharts` object match the names of each step's `background` in the ArchieML doc:
|
||||
|
||||
```yaml
|
||||
# ArchieML doc
|
||||
[blocks]
|
||||
type: ai-scroller
|
||||
id: my-map-scroller
|
||||
width: fluid
|
||||
foregroundPosition: right
|
||||
stackBackground: true
|
||||
|
||||
# Array of step objects
|
||||
[.steps]
|
||||
background: AiMap1
|
||||
foreground: #### Step 1
|
||||
|
||||
Here's where something happend.
|
||||
:end
|
||||
altText: A map showing the Upper West side in New York City.
|
||||
|
||||
Can add paragraphs of alt text if you want to break up sentences.
|
||||
:end
|
||||
|
||||
background: AiMap2
|
||||
foreground: #### Step 2
|
||||
|
||||
Something happened on some street...
|
||||
:end
|
||||
altText: The same map now highlights 98th Street.
|
||||
:end
|
||||
|
||||
background: AiMap3
|
||||
foreground: #### Step 3
|
||||
|
||||
... and now there are multiple protests.
|
||||
:end
|
||||
altText: The same map now highlights three locations near 98th Street where something particulary important happened.
|
||||
:end
|
||||
[]
|
||||
[]
|
||||
```
|
||||
|
||||
Then parse the relevant ArchieML block object before passing to the `Scroller` component.
|
||||
|
||||
```svelte
|
||||
<!-- App.svelte -->
|
||||
{#each content.blocks as block}
|
||||
{#if block.type === 'ai-scroller'}
|
||||
<Scroller
|
||||
id={block.id}
|
||||
backgroundWidth={block.width}
|
||||
foregroundPosition={block.foregroundPosition}
|
||||
stackBackground={truthy(block.stackBackground)}
|
||||
steps={block.steps.map((step) => ({
|
||||
background: aiCharts[step.background],
|
||||
backgroundProps: { assetsPath: assets || '/' },
|
||||
foreground: step.foreground,
|
||||
altText: step.altText,
|
||||
}))}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
```
|
||||
|
||||
> **Note:** Some props, like `stackBackground`, expect boolean values. If you're using the graphics kit, use the `truthy()` util function to convert a string value to a boolean.
|
||||
|
||||
> **Note:** In the graphics kit, the image source paths in ai2svelte components have to be fixed by passing `assets` to each step object, like in the example above.
|
||||
|
||||
## Custom foreground
|
||||
|
||||
[Demo](?path=/story/components-graphics-scroller--custom-foreground)
|
||||
|
||||
Instead of just text, you can use components as foregrounds, and optionally pass props to it.
|
||||
|
||||
If you're customising your own foreground component, remember to add alt text that describes the background graphic.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import MyBackground from './MyBackground.svelte'; // Your own background component
|
||||
import MyInteractiveForeground from './MyInteractiveForeground.svelte'; // Your custom foreground component
|
||||
|
||||
const steps = [
|
||||
{
|
||||
background: MyBackground,
|
||||
backgroundProps: { colour: 'red' }, // Props for your background component, if needed
|
||||
foreground: MyInteractiveForeground, // Custom foreground component
|
||||
},
|
||||
{
|
||||
background: MyBackground,
|
||||
backgroundProps: { colour: 'blue' },
|
||||
foreground: '#### Step 2\n\nLorem ipsum blue', // You can still add a markdown string as foreground; you can mix and match
|
||||
},
|
||||
{
|
||||
background: MyBackground,
|
||||
backgroundProps: { colour: 'green' },
|
||||
foreground: MyInteractiveForeground,
|
||||
foregroundProps: { count: 100 }, // Props for your custom foreground component, if needed
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<Scroller {steps} />
|
||||
```
|
||||
|
||||
## Custom foreground with ArchieML
|
||||
|
||||
[Demo](?path=/story/components-graphics-scroller--customforeground-archie-ml)
|
||||
|
||||
You can use custom foreground components with ArchieML with a few additional steps.
|
||||
|
||||
In your graphics kit project's `App.svelte`, import your custom foregroud components and add them to a `foregroundComponents` object, just as you import ai2svelte background graphics and add them to the `aiCharts` object:
|
||||
|
||||
```svelte
|
||||
<!-- App.svelte -->
|
||||
<script>
|
||||
import content from '$locales/en/content.json';
|
||||
|
||||
// Background ai2svelte graphics
|
||||
import AiMap1 from './ai2svelte/my-map-1.svelte';
|
||||
import AiMap2 from './ai2svelte/my-map-2.svelte';
|
||||
import AiMap3 from './ai2svelte/my-map-3.svelte';
|
||||
|
||||
// Foreground components, which can be ai2svelte or not.
|
||||
import Foreground1 from './ai2svelte/my-foreground-1.svelte';
|
||||
|
||||
// Graphics kit only
|
||||
import { assets } from '$app/paths'; // 👈 If using in the graphics kit...
|
||||
import { truthy } from '$utils/propValidators'; // 👈 If using in the graphics kit...
|
||||
|
||||
// Background ai2svelte graphics components
|
||||
const aiCharts = {
|
||||
AiMap1,
|
||||
AiMap2,
|
||||
AiMap3,
|
||||
// Other charts...
|
||||
};
|
||||
|
||||
// Foreground components
|
||||
const foregroundComponents = {
|
||||
Foreground1,
|
||||
// Other components...
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Then add the following structure to your ArchieML Doc, making sure that the names of your charts in the `aiCharts` and `foregroundComponents` objects match the names of each step's `background` and `foreground` in the ArchieML doc:
|
||||
|
||||
```yaml
|
||||
# ArchieML doc
|
||||
[blocks]
|
||||
type: ai-scroller
|
||||
id: my-map-scroller
|
||||
foregroundPosition: left
|
||||
stackBackground: true
|
||||
|
||||
# Array of step objects
|
||||
[.steps]
|
||||
background: AiMap1
|
||||
# You can still use a markdown string even if other step/s use a custom foreground component
|
||||
foreground: #### Step 1
|
||||
|
||||
Here's where something happend.
|
||||
:end
|
||||
altText: A map showing the Upper West side in New York City.
|
||||
:end
|
||||
|
||||
background: AiMap2
|
||||
foreground: Foreground1 # The name of your custom foreground component
|
||||
altText: The same map now highlights 98th Street.
|
||||
:end
|
||||
background: AiMap3
|
||||
foreground: #### Step 3
|
||||
|
||||
... and now there are multiple protests.
|
||||
:end
|
||||
altText: The same map now highlights three locations near 98th Street where something particulary important happened.
|
||||
:end
|
||||
[]
|
||||
[]
|
||||
```
|
||||
|
||||
Then parse the relevant ArchieML block object before passing to the `Scroller` component.
|
||||
|
||||
```svelte
|
||||
<!-- App.svelte -->
|
||||
{#each content.blocks as block}
|
||||
{#if block.type === 'ai-scroller'}
|
||||
<Scroller
|
||||
id={block.id}
|
||||
backgroundWidth={block.width}
|
||||
foregroundPosition={block.foregroundPosition}
|
||||
stackBackground={truthy(block.stackBackground)}
|
||||
steps={block.steps.map((step) => ({
|
||||
background: aiCharts[step.background],
|
||||
backgroundProps: { assetsPath: assets || '/' },
|
||||
foreground: foregroundComponents[step.foreground] || step.foreground,
|
||||
foregroundProps: { assetsPath: assets || '/' },
|
||||
altText: step.altText,
|
||||
}))}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
```
|
||||
|
||||
> **Note:** You only need to pass `foregroundProps: { assetsPath: assets || '/' }` in the graphics kit if your foreground components are ai2svelte graphicss.
|
||||
177
src/components/SvelteScroller/SvelteScroller.stories.svelte
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from '@storybook/addon-svelte-csf';
|
||||
import SvelteScroller from './SvelteScroller.svelte';
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: 'Components/Graphics/SvelteScroller',
|
||||
component: SvelteScroller,
|
||||
argTypes: {
|
||||
steps: { control: false },
|
||||
backgroundWidth: {
|
||||
control: 'select',
|
||||
options: ['normal', 'wide', 'wider', 'widest', 'fluid'],
|
||||
},
|
||||
foregroundPosition: {
|
||||
control: 'select',
|
||||
options: ['middle', 'left', 'right', 'left opposite', 'right opposite'],
|
||||
},
|
||||
embeddedLayout: {
|
||||
control: 'select',
|
||||
options: ['fb', 'bf'],
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import MyBackground from './demo/components/basic/Step.svelte';
|
||||
import MyInteractiveForeground from './demo/components/basic/InteractiveForeground.svelte';
|
||||
|
||||
// ai2svelte backgrounds
|
||||
import AiMap1 from './demo/components/ai2svelte/ai-scroller-1.svelte';
|
||||
import AiMap2 from './demo/components/ai2svelte/ai-scroller-2.svelte';
|
||||
import AiMap3 from './demo/components/ai2svelte/ai-scroller-3.svelte';
|
||||
|
||||
// ai2svelte foreground
|
||||
import AiForeground from './demo/components/ai2svelte/ai-foreground.svelte';
|
||||
|
||||
const aiCharts = {
|
||||
AiMap1,
|
||||
AiMap2,
|
||||
AiMap3,
|
||||
};
|
||||
|
||||
const foregroundComponents = {
|
||||
AiForeground,
|
||||
};
|
||||
|
||||
const docBlock = {
|
||||
foregroundPosition: 'right',
|
||||
id: 'my-scroller',
|
||||
stackBackground: 'true',
|
||||
steps: [
|
||||
{
|
||||
background: aiCharts.AiMap1,
|
||||
foreground: "#### Step 1\n\nHere's where something happend.",
|
||||
altText: 'A map showing the Upper West side in New York City.',
|
||||
},
|
||||
{
|
||||
background: aiCharts.AiMap2,
|
||||
foreground: '#### Step 2\n\nSomething happened on some street...',
|
||||
altText: 'The same map now highlights 98th Street.',
|
||||
},
|
||||
{
|
||||
background: aiCharts.AiMap3,
|
||||
foreground: '#### Step 3\n\n... and now there are multiple protests.',
|
||||
altText:
|
||||
'The same map now highlights three locations near 98th Street where something particulary important happened.',
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
const docBlockCustomForeground = {
|
||||
foregroundPosition: 'left',
|
||||
id: 'my-scroller',
|
||||
stackBackground: 'true',
|
||||
steps: [
|
||||
{
|
||||
background: aiCharts.AiMap1,
|
||||
foreground: "#### Step 1\n\nHere's where something happend.",
|
||||
altText: 'A map showing the Upper West side in New York City.',
|
||||
},
|
||||
{
|
||||
background: aiCharts.AiMap2,
|
||||
foreground: foregroundComponents.AiForeground,
|
||||
altText: 'The same map now highlights 98th Street.',
|
||||
},
|
||||
{
|
||||
background: aiCharts.AiMap3,
|
||||
foreground: '#### Step 3\n\n... and now there are multiple protests.',
|
||||
altText:
|
||||
'The same map now highlights three locations near 98th Street where something particulary important happened.',
|
||||
},
|
||||
],
|
||||
} 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="ArchieML and ai2svelte" exportName="ArchieML">
|
||||
<Scroller
|
||||
id={docBlock.id}
|
||||
foregroundPosition={docBlock.foregroundPosition}
|
||||
stackBackground={docBlock.stackBackground === 'true'}
|
||||
steps={docBlock.steps.map((step) => ({
|
||||
background: step.background,
|
||||
foreground: step.foreground,
|
||||
altText: step.altText,
|
||||
}))}
|
||||
/>
|
||||
</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
|
||||
name="Custom foreground with ArchiemL"
|
||||
exportName="CustomforegroundArchieML"
|
||||
>
|
||||
<SvelteScroller
|
||||
id={docBlockCustomForeground.id}
|
||||
foregroundPosition={docBlockCustomForeground.foregroundPosition}
|
||||
stackBackground={docBlockCustomForeground.stackBackground === 'true'}
|
||||
steps={docBlockCustomForeground.steps.map((step) => ({
|
||||
background: step.background,
|
||||
foreground: step.foreground,
|
||||
altText: step.altText,
|
||||
}))}
|
||||
/>
|
||||
</Story>
|
||||
189
src/components/SvelteScroller/SvelteScroller.svelte
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
<!-- @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';
|
||||
|
||||
// Types
|
||||
import type {
|
||||
ContainerWidth,
|
||||
ForegroundPosition,
|
||||
ScrollerStep,
|
||||
} from '../@types/global';
|
||||
|
||||
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)
|
||||
*/
|
||||
top?: number;
|
||||
/**
|
||||
* Bottom prop passed to [svelte-scroller](https://github.com/sveltejs/svelte-scroller#parameters)
|
||||
*/
|
||||
bottom?: number;
|
||||
/**
|
||||
* Parallax prop passed to [svelte-scroller](https://github.com/sveltejs/svelte-scroller#parameters)
|
||||
*/
|
||||
parallax?: boolean;
|
||||
/** ID of the scroller container */
|
||||
id?: string;
|
||||
/** Set a class to target with SCSS */
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
id = '',
|
||||
steps,
|
||||
backgroundWidth = 'fluid',
|
||||
foregroundPosition = 'middle',
|
||||
stackBackground = true,
|
||||
preload = 1,
|
||||
embedded = false,
|
||||
embeddedLayout = 'fb',
|
||||
threshold = 0.5,
|
||||
top = 0,
|
||||
bottom = 1,
|
||||
parallax = false,
|
||||
class: cls = '',
|
||||
}: Props = $props();
|
||||
|
||||
// Bindable variables passed to ScrollerBase
|
||||
let index = $state(0);
|
||||
let offset = $state(0);
|
||||
let progress = $state(0);
|
||||
</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}
|
||||
|
||||
<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;
|
||||
}
|
||||
}
|
||||
|
||||
div.foreground {
|
||||
&.right {
|
||||
width: 50%;
|
||||
float: right;
|
||||
@media (max-width: 1200px) {
|
||||
width: 100%;
|
||||
float: initial;
|
||||
}
|
||||
}
|
||||
|
||||
&.left {
|
||||
width: 50%;
|
||||
float: left;
|
||||
@media (max-width: 1200px) {
|
||||
width: 100%;
|
||||
float: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<script>
|
||||
// Hard-coding for demo purposes only...
|
||||
import stepXs from './images/Body-issues-key-xs.png';
|
||||
let width = $state();
|
||||
</script>
|
||||
|
||||
<div id="g-Body-issues-key-box" bind:clientWidth={width}>
|
||||
<!-- Artboard: xs -->
|
||||
{#if width && width >= 0}
|
||||
<div id="g-Body-issues-key-xs" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 48.4848% 0;"></div>
|
||||
<div
|
||||
id="g-Body-issues-key-xs-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepXs});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai0-1"
|
||||
class="g-Layer_1 g-aiAbs g-aiPointText"
|
||||
style="top:19.4775%;margin-top:-10.2px;left:8.1818%;width:276px;"
|
||||
>
|
||||
<p class="g-pstyle0">Likelihood of something happening</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="g-ai0-3"
|
||||
class="g-Layer_1 g-aiAbs g-aiPointText"
|
||||
style="top:55.1025%;margin-top:-10.2px;left:27.2727%;width:68px;"
|
||||
>
|
||||
<p class="g-pstyle1">0-25%</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-4"
|
||||
class="g-Layer_1 g-aiAbs g-aiPointText"
|
||||
style="top:55.1025%;margin-top:-10.2px;left:74.2424%;width:75px;"
|
||||
>
|
||||
<p class="g-pstyle1">50-75%</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-5"
|
||||
class="g-Layer_1 g-aiAbs g-aiPointText"
|
||||
style="top:79.4775%;margin-top:-10.2px;left:74.2424%;width:82px;"
|
||||
>
|
||||
<p class="g-pstyle1">75-100%</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-6"
|
||||
class="g-Layer_1 g-aiAbs g-aiPointText"
|
||||
style="top:83.2275%;margin-top:-10.2px;left:27.2727%;width:77px;"
|
||||
>
|
||||
<p class="g-pstyle1">25-50%</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- End ai2html - 2025-03-17 09:52 -->
|
||||
|
||||
<!-- Generated by ai2html v0.100.0 - 2025-03-17 09:52 -->
|
||||
<!-- ai file: Body-issues-key.ai -->
|
||||
<style lang="scss">
|
||||
#g-Body-issues-key-box,
|
||||
#g-Body-issues-key-box .g-artboard {
|
||||
margin: 0 auto;
|
||||
}
|
||||
#g-Body-issues-key-box p {
|
||||
margin: 0;
|
||||
}
|
||||
#g-Body-issues-key-box .g-aiAbs {
|
||||
position: absolute;
|
||||
}
|
||||
#g-Body-issues-key-box .g-aiImg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#g-Body-issues-key-box .g-aiPointText p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#g-Body-issues-key-xs {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-Body-issues-key-xs p {
|
||||
font-family: 'Knowledge', 'Source Sans Pro', Arial, sans-serif;
|
||||
font-weight: 500;
|
||||
line-height: 19px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(64, 64, 64);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-Body-issues-key-xs .g-pstyle0 {
|
||||
height: 19px;
|
||||
}
|
||||
#g-Body-issues-key-xs .g-pstyle1 {
|
||||
font-weight: 400;
|
||||
height: 19px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
<script lang="ts">
|
||||
// Hard-coding for demo purposes only...
|
||||
import stepXl from './images/step-1-xl.png';
|
||||
import stepLg from './images/step-1-lg.png';
|
||||
import stepMd from './images/step-1-md.png';
|
||||
import stepSm from './images/step-1-sm.png';
|
||||
import stepXs from './images/step-1-xs.png';
|
||||
let width = $state<number>();
|
||||
</script>
|
||||
|
||||
<!-- Generated by ai2html v0.100.0 - 2021-09-30 14:21 -->
|
||||
|
||||
<div id="g-step-1-box" bind:clientWidth={width}>
|
||||
<!-- Artboard: XL -->
|
||||
{#if width && width >= 1200}
|
||||
<div id="g-step-1-xl" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 50% 0;"></div>
|
||||
<div
|
||||
id="g-step-1-xl-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepXl});"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: LG -->
|
||||
{#if width && width >= 930 && width < 1200}
|
||||
<div id="g-step-1-lg" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.3763% 0;"></div>
|
||||
<div
|
||||
id="g-step-1-lg-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepLg});"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: MD -->
|
||||
{#if width && width >= 660 && width < 930}
|
||||
<div id="g-step-1-md" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.7576% 0;"></div>
|
||||
<div
|
||||
id="g-step-1-md-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepMd});"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: SM -->
|
||||
{#if width && width >= 510 && width < 660}
|
||||
<div id="g-step-1-sm" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 49.6078% 0;"></div>
|
||||
<div
|
||||
id="g-step-1-sm-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepSm});"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: XS -->
|
||||
{#if width && width >= 0 && width < 510}
|
||||
<div id="g-step-1-xs" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.4545% 0;"></div>
|
||||
<div
|
||||
id="g-step-1-xs-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepXs});"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- End ai2html - 2021-09-30 14:21 -->
|
||||
|
||||
<!-- ai file: step-1.ai -->
|
||||
|
||||
<!-- svelte-ignore css_unused_selector -->
|
||||
<style lang="scss">
|
||||
#g-step-1-box,
|
||||
#g-step-1-box .g-artboard {
|
||||
margin: 0 auto;
|
||||
}
|
||||
#g-step-1-box p {
|
||||
margin: 0;
|
||||
}
|
||||
#g-step-1-box .g-aiAbs {
|
||||
position: absolute;
|
||||
}
|
||||
#g-step-1-box .g-aiImg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#g-step-1-box .g-aiPointText p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#g-step-1-xl {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-1-lg {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-1-md {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-1-sm {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-1-xs {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Custom CSS */
|
||||
</style>
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
<script lang="ts">
|
||||
// Hard-coding for demo purposes only...
|
||||
// @ts-ignore img
|
||||
import stepXl from './images/step-2-xl.png';
|
||||
// @ts-ignore img
|
||||
import stepLg from './images/step-2-lg.png';
|
||||
// @ts-ignore img
|
||||
import stepMd from './images/step-2-md.png';
|
||||
// @ts-ignore img
|
||||
import stepSm from './images/step-2-sm.png';
|
||||
// @ts-ignore img
|
||||
import stepXs from './images/step-2-xs.png';
|
||||
let width = $state<null | number>(null);
|
||||
</script>
|
||||
|
||||
<!-- Generated by ai2html v0.100.0 - 2021-09-30 14:20 -->
|
||||
|
||||
<div id="g-step-2-box" bind:clientWidth={width}>
|
||||
<!-- Artboard: XL -->
|
||||
{#if width && width >= 1200}
|
||||
<div id="g-step-2-xl" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 50% 0;"></div>
|
||||
<div
|
||||
id="g-step-2-xl-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepXl});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai0-1"
|
||||
class="g-annotation g-aiAbs g-aiPointText"
|
||||
style="top:26.183%;margin-top:-21.1px;left:55.6774%;width:180px;"
|
||||
>
|
||||
<p class="g-pstyle0">This thing here is</p>
|
||||
<p class="g-pstyle0">particularly important</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: LG -->
|
||||
{#if width && width >= 930 && width < 1200}
|
||||
<div id="g-step-2-lg" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.3763% 0;"></div>
|
||||
<div
|
||||
id="g-step-2-lg-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepLg});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai1-1"
|
||||
class="g-annotation g-aiAbs g-aiPointText"
|
||||
style="top:25.8206%;margin-top:-21px;left:56.2692%;width:162px;"
|
||||
>
|
||||
<p class="g-pstyle0">This thing here is</p>
|
||||
<p class="g-pstyle0">particularly important</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: MD -->
|
||||
{#if width && width >= 660 && width < 930}
|
||||
<div id="g-step-2-md" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.7576% 0;"></div>
|
||||
<div
|
||||
id="g-step-2-md-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepMd});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai2-1"
|
||||
class="g-annotation g-aiAbs g-aiPointText"
|
||||
style="top:47.5478%;margin-top:-21px;left:32.4915%;width:162px;"
|
||||
>
|
||||
<p class="g-pstyle0">This thing here is</p>
|
||||
<p class="g-pstyle0">particularly important</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: SM -->
|
||||
{#if width && width >= 510 && width < 660}
|
||||
<div id="g-step-2-sm" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 49.6078% 0;"></div>
|
||||
<div
|
||||
id="g-step-2-sm-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepSm});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai3-1"
|
||||
class="g-annotation g-aiAbs g-aiPointText"
|
||||
style="top:55.3265%;margin-top:-21px;left:30.7585%;width:162px;"
|
||||
>
|
||||
<p class="g-pstyle0">This thing here is</p>
|
||||
<p class="g-pstyle0">particularly important</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: XS -->
|
||||
{#if width && width >= 0 && width < 510}
|
||||
<div id="g-step-2-xs" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.4545% 0;"></div>
|
||||
<div
|
||||
id="g-step-2-xs-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepXs});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai4-1"
|
||||
class="g-annotation g-aiAbs g-aiPointText"
|
||||
style="top:22.3913%;margin-top:-21px;left:52.5519%;width:162px;"
|
||||
>
|
||||
<p class="g-pstyle0">This thing here is</p>
|
||||
<p class="g-pstyle0">particularly important</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- End ai2html - 2021-09-30 14:20 -->
|
||||
|
||||
<!-- ai file: step-2.ai -->
|
||||
|
||||
<style lang="scss">
|
||||
#g-step-2-box,
|
||||
#g-step-2-box .g-artboard {
|
||||
margin: 0 auto;
|
||||
}
|
||||
#g-step-2-box p {
|
||||
margin: 0;
|
||||
}
|
||||
#g-step-2-box .g-aiAbs {
|
||||
position: absolute;
|
||||
}
|
||||
#g-step-2-box .g-aiImg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#g-step-2-box .g-aiPointText p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#g-step-2-xl {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-2-xl p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 18px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-2-xl .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-2-lg {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-2-lg p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-2-lg .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-2-md {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-2-md p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-2-md .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-2-sm {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-2-sm p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-2-sm .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-2-xs {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-2-xs p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-2-xs .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Custom CSS */
|
||||
</style>
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
<script lang="ts">
|
||||
// Hard-coding for demo purposes only...
|
||||
// @ts-ignore img
|
||||
import stepXl from './images/step-3-xl.png';
|
||||
// @ts-ignore img
|
||||
import stepLg from './images/step-3-lg.png';
|
||||
// @ts-ignore img
|
||||
import stepMd from './images/step-3-md.png';
|
||||
// @ts-ignore img
|
||||
import stepSm from './images/step-3-sm.png';
|
||||
// @ts-ignore img
|
||||
import stepXs from './images/step-3-xs.png';
|
||||
let width = $state<null | number>(null);
|
||||
</script>
|
||||
|
||||
<!-- Generated by ai2html v0.100.0 - 2021-09-30 14:28 -->
|
||||
|
||||
<div id="g-step-3-box" bind:clientWidth={width}>
|
||||
<!-- Artboard: XL -->
|
||||
{#if width && width >= 1200}
|
||||
<div id="g-step-3-xl" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 50% 0;"></div>
|
||||
<div
|
||||
id="g-step-3-xl-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepXl});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai0-1"
|
||||
class="g-step-3 g-aiAbs g-aiPointText"
|
||||
style="top:19.683%;margin-top:-21.1px;left:54.3441%;width:130px;"
|
||||
>
|
||||
<p class="g-pstyle0">Something</p>
|
||||
<p class="g-pstyle0">happened here</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: LG -->
|
||||
{#if width && width >= 930 && width < 1200}
|
||||
<div id="g-step-3-lg" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.3763% 0;"></div>
|
||||
<div
|
||||
id="g-step-3-lg-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepLg});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai1-1"
|
||||
class="g-step-3 g-aiAbs g-aiPointText"
|
||||
style="top:27.5682%;margin-top:-21px;left:54.9127%;width:118px;"
|
||||
>
|
||||
<p class="g-pstyle0">Something</p>
|
||||
<p class="g-pstyle0">happened here</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: MD -->
|
||||
{#if width && width >= 660 && width < 930}
|
||||
<div id="g-step-3-md" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.7576% 0;"></div>
|
||||
<div
|
||||
id="g-step-3-md-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepMd});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai2-1"
|
||||
class="g-step-3 g-aiAbs g-aiPointText"
|
||||
style="top:23.3631%;margin-top:-21px;left:50.4963%;width:118px;"
|
||||
>
|
||||
<p class="g-pstyle0">Something</p>
|
||||
<p class="g-pstyle0">happened here</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: SM -->
|
||||
{#if width && width >= 510 && width < 660}
|
||||
<div id="g-step-3-sm" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 49.6078% 0;"></div>
|
||||
<div
|
||||
id="g-step-3-sm-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepSm});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai3-1"
|
||||
class="g-step-3 g-aiAbs g-aiPointText"
|
||||
style="top:20.1486%;margin-top:-21px;left:55.7925%;width:118px;"
|
||||
>
|
||||
<p class="g-pstyle0">Something</p>
|
||||
<p class="g-pstyle0">happened here</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: XS -->
|
||||
{#if width && width >= 0 && width < 510}
|
||||
<div id="g-step-3-xs" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 55.4545% 0;"></div>
|
||||
<div
|
||||
id="g-step-3-xs-img"
|
||||
class="g-aiImg"
|
||||
style="background-image: url({stepXs});"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai4-1"
|
||||
class="g-step-3 g-aiAbs g-aiPointText"
|
||||
style="top:42.0634%;margin-top:-21px;left:27.3523%;width:118px;"
|
||||
>
|
||||
<p class="g-pstyle0">Something</p>
|
||||
<p class="g-pstyle0">happened here</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- End ai2html - 2021-09-30 14:28 -->
|
||||
|
||||
<!-- ai file: step-3.ai -->
|
||||
|
||||
<style lang="scss">
|
||||
#g-step-3-box,
|
||||
#g-step-3-box .g-artboard {
|
||||
margin: 0 auto;
|
||||
}
|
||||
#g-step-3-box p {
|
||||
margin: 0;
|
||||
}
|
||||
#g-step-3-box .g-aiAbs {
|
||||
position: absolute;
|
||||
}
|
||||
#g-step-3-box .g-aiImg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#g-step-3-box .g-aiPointText p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#g-step-3-xl {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-3-xl p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 18px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-3-xl .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-3-lg {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-3-lg p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-3-lg .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-3-md {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-3-md p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-3-md .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-3-sm {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-3-sm p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-3-sm .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
#g-step-3-xs {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-step-3-xs p {
|
||||
font-family:
|
||||
'Source Sans Pro',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-step-3-xs .g-pstyle0 {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Custom CSS */
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 481 KiB |
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 657 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 488 KiB |
|
After Width: | Height: | Size: 335 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 666 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 597 KiB |
|
After Width: | Height: | Size: 434 KiB |
|
After Width: | Height: | Size: 267 KiB |
|
After Width: | Height: | Size: 776 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
interface Props {
|
||||
count?: number;
|
||||
}
|
||||
|
||||
let { count = $bindable(0) }: Props = $props();
|
||||
</script>
|
||||
|
||||
<h4>Interactive step</h4>
|
||||
|
||||
<p class="font-sans">The count is <strong>{count}</strong></p>
|
||||
|
||||
<button
|
||||
onclick={() => {
|
||||
count += 1;
|
||||
}}>Click Me</button
|
||||
>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
interface Props {
|
||||
colour?: string;
|
||||
}
|
||||
|
||||
let { colour = 'lightblue' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="step" style={`background: ${colour};`}></div>
|
||||
|
||||
<style lang="scss">
|
||||
.step {
|
||||
width: 100vw;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -40,6 +40,7 @@ export { default as SiteFooter } from './components/SiteFooter/SiteFooter.svelte
|
|||
export { default as SiteHeader } from './components/SiteHeader/SiteHeader.svelte';
|
||||
export { default as SiteHeadline } from './components/SiteHeadline/SiteHeadline.svelte';
|
||||
export { default as Spinner } from './components/Spinner/Spinner.svelte';
|
||||
export { default as SvelteScroller } from './components/SvelteScroller/SvelteScroller.svelte';
|
||||
export { default as SponsorshipAd } from './components/AdSlot/SponsorshipAd.svelte';
|
||||
export { default as Table } from './components/Table/Table.svelte';
|
||||
export { default as Theme, themes } from './components/Theme/Theme.svelte';
|
||||
|
|
|
|||