Merge remote-tracking branch 'playground/master'

This commit is contained in:
Kuba Orlik 2026-08-25 14:16:33 +02:00
commit 9e76f00246
23 changed files with 693 additions and 30 deletions

14
package-lock.json generated
View File

@ -17,7 +17,7 @@
"@sealcode/jdd": "^0.8.13", "@sealcode/jdd": "^0.8.13",
"@sealcode/jdd-editor": "^0.2.37", "@sealcode/jdd-editor": "^0.2.37",
"@sealcode/monaco-wrapper": "^0.0.12", "@sealcode/monaco-wrapper": "^0.0.12",
"@sealcode/sealgen": "^0.19.23", "@sealcode/sealgen": "^0.19.25",
"@sealcode/show-first-row": "^0.1.0", "@sealcode/show-first-row": "^0.1.0",
"@sealcode/simplemde": "^1.12.1", "@sealcode/simplemde": "^1.12.1",
"@sealcode/sortable": "^0.0.1", "@sealcode/sortable": "^0.0.1",
@ -1463,9 +1463,9 @@
"integrity": "sha512-N4BuMuantv2A63PbFwLg53AJbDZ2/BzKxpgBIRd7jDWyZLw58en6FFZgcbXMLDU7gwKNYfyLOJMLwXyn6aEsnQ==" "integrity": "sha512-N4BuMuantv2A63PbFwLg53AJbDZ2/BzKxpgBIRd7jDWyZLw58en6FFZgcbXMLDU7gwKNYfyLOJMLwXyn6aEsnQ=="
}, },
"node_modules/@sealcode/sealgen": { "node_modules/@sealcode/sealgen": {
"version": "0.19.23", "version": "0.19.25",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.23.tgz", "resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.25.tgz",
"integrity": "sha512-jKMp4LdOWC5lwOzHC6bT9y98NxZ5U6CpzdjoqtghwBIZZQh9Z58FX5+wgLO+u6Vzec7+7Gyt3097x6r4L18DdA==", "integrity": "sha512-ryWLuKn9S53XDsSEmttWjM57Wa/thrOEaMvM2uy5xOYkBVo9saTTBFmInm53In1nnrh44kNkuYogBLG4dXhNfw==",
"dependencies": { "dependencies": {
"@koa/router": "^12.0.1", "@koa/router": "^12.0.1",
"@sealcode/file-manager": "^1.0.2", "@sealcode/file-manager": "^1.0.2",
@ -16383,9 +16383,9 @@
"integrity": "sha512-N4BuMuantv2A63PbFwLg53AJbDZ2/BzKxpgBIRd7jDWyZLw58en6FFZgcbXMLDU7gwKNYfyLOJMLwXyn6aEsnQ==" "integrity": "sha512-N4BuMuantv2A63PbFwLg53AJbDZ2/BzKxpgBIRd7jDWyZLw58en6FFZgcbXMLDU7gwKNYfyLOJMLwXyn6aEsnQ=="
}, },
"@sealcode/sealgen": { "@sealcode/sealgen": {
"version": "0.19.23", "version": "0.19.25",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.23.tgz", "resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.25.tgz",
"integrity": "sha512-jKMp4LdOWC5lwOzHC6bT9y98NxZ5U6CpzdjoqtghwBIZZQh9Z58FX5+wgLO+u6Vzec7+7Gyt3097x6r4L18DdA==", "integrity": "sha512-ryWLuKn9S53XDsSEmttWjM57Wa/thrOEaMvM2uy5xOYkBVo9saTTBFmInm53In1nnrh44kNkuYogBLG4dXhNfw==",
"requires": { "requires": {
"@koa/router": "^12.0.1", "@koa/router": "^12.0.1",
"@sealcode/file-manager": "^1.0.2", "@sealcode/file-manager": "^1.0.2",

View File

@ -104,7 +104,7 @@
"@sealcode/jdd": "^0.8.13", "@sealcode/jdd": "^0.8.13",
"@sealcode/jdd-editor": "^0.2.37", "@sealcode/jdd-editor": "^0.2.37",
"@sealcode/monaco-wrapper": "^0.0.12", "@sealcode/monaco-wrapper": "^0.0.12",
"@sealcode/sealgen": "^0.19.23", "@sealcode/sealgen": "^0.19.25",
"@sealcode/show-first-row": "^0.1.0", "@sealcode/show-first-row": "^0.1.0",
"@sealcode/simplemde": "^1.12.1", "@sealcode/simplemde": "^1.12.1",
"@sealcode/sortable": "^0.0.1", "@sealcode/sortable": "^0.0.1",

View File

@ -12,6 +12,7 @@ export function button({
submit = false, submit = false,
icon, icon,
centered = false, centered = false,
classes = [],
}: { }: {
text: string; text: string;
href: string; href: string;
@ -20,6 +21,7 @@ export function button({
submit?: boolean; submit?: boolean;
icon?: string; icon?: string;
centered?: boolean; centered?: boolean;
classes?: string[];
}): JSX.Element { }): JSX.Element {
const classstr = `button button--color-${variant}`; const classstr = `button button--color-${variant}`;
if (submit) { if (submit) {
@ -33,6 +35,7 @@ export function button({
"button", "button",
`button--color-${variant}`, `button--color-${variant}`,
{ "button--centered": centered }, { "button--centered": centered },
...classes,
]} ]}
href={disabled ? false : href} href={disabled ? false : href}
disabled={disabled} disabled={disabled}

View File

@ -14,7 +14,7 @@ export async function sendEml(
for (const [key, value] of Object.entries(template_data)) { for (const [key, value] of Object.entries(template_data)) {
eml_content = eml_content.replaceAll(`{{${key}}}`, htmlEscape(value)); eml_content = eml_content.replaceAll(`{{${key}}}`, htmlEscape(value));
} }
const parsed = await simpleParser(eml_content); const parsed = await simpleParser(eml_content, { skipImageLinks: true });
const mailer = context.$app.mailer; const mailer = context.$app.mailer;

View File

@ -0,0 +1,20 @@
import { TempstreamJSX } from "tempstream";
import type { ComponentToHTMLArgs } from "@sealcode/jdd";
import { Component, ComponentArguments } from "@sealcode/jdd";
const component_arguments = {
id: new ComponentArguments.ShortText(),
} as const;
export class Anchor extends Component<typeof component_arguments> {
getArguments() {
return component_arguments;
}
async toHTML({
args: { id },
classes,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
return <div id={id} class={["anchor", "jdd-no-gap", ...classes]}></div>;
}
}

View File

@ -7,6 +7,18 @@
padding: var(--vertical-padding) 0; padding: var(--vertical-padding) 0;
&.background-wrapper--bleed-none {
grid-column: content !important;
}
&.background-wrapper--bleed-bleed {
grid-column: bleed !important;
}
&.background-wrapper--bleed-screen {
grid-column: 1/-1 !important;
}
&.background-wrapper--padding-no { &.background-wrapper--padding-no {
padding: 0; padding: 0;
} }
@ -37,4 +49,30 @@
color: var(--color-brand-text-on-accent2); color: var(--color-brand-text-on-accent2);
--color-brand-text-fg: var(--color-brand-text-on-accent2); --color-brand-text-fg: var(--color-brand-text-on-accent2);
} }
&.background-wrapper--frame1 {
padding: 16px;
border: 16px dashed var(--color-brand-accent);
background-color: color-mix(
in srgb,
var(--color-brand-accent) 20%,
transparent 80%
);
--color-brand-text-bg: transparent;
}
&.background-wrapper--frame2 {
padding: 16px;
border: 16px dashed var(--color-brand-accent2);
background-color: color-mix(
in srgb,
var(--color-brand-accent2) 20%,
transparent 80%
);
--color-brand-text-bg: transparent;
}
&.background-wrapper--crooked {
transform: scale(1.05) rotate(-2deg);
}
} }

View File

@ -6,7 +6,7 @@ import type {
} from "@sealcode/jdd"; } from "@sealcode/jdd";
import { Component, ComponentArguments } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd";
export const background_variants = ["variant1", "variant2"]; export const background_variants = ["variant1", "variant2", "frame1", "frame2"];
const component_arguments = { const component_arguments = {
variant: new ComponentArguments.Enum(background_variants), variant: new ComponentArguments.Enum(background_variants),
@ -14,6 +14,10 @@ const component_arguments = {
padding: new ComponentArguments.Boolean(), padding: new ComponentArguments.Boolean(),
big_padding: new ComponentArguments.Boolean(), big_padding: new ComponentArguments.Boolean(),
margin: new ComponentArguments.Boolean(), margin: new ComponentArguments.Boolean(),
bleed: new ComponentArguments.Enum(["none", "bleed", "screen"]).setDefaultValue(
"screen"
),
crooked: new ComponentArguments.Boolean().setDefaultValue(false),
content: new ComponentArguments.NestedComponent(), content: new ComponentArguments.NestedComponent(),
} as const; } as const;
@ -31,7 +35,16 @@ export class BackgroundWrapper extends Component<typeof component_arguments> {
} }
async toHTML({ async toHTML({
args: { variant, content, grow_to_screen_height, padding, margin, big_padding }, args: {
variant,
content,
grow_to_screen_height,
padding,
margin,
big_padding,
bleed,
crooked,
},
classes, classes,
jdd_context, jdd_context,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> { }: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
@ -40,10 +53,12 @@ export class BackgroundWrapper extends Component<typeof component_arguments> {
class={[ class={[
"background-wrapper", "background-wrapper",
`background-wrapper--${variant}`, `background-wrapper--${variant}`,
`background-wrapper--bleed-${bleed}`,
`background-wrapper--padding-${padding ? "yes" : "no"}`, `background-wrapper--padding-${padding ? "yes" : "no"}`,
`background-wrapper--big-padding-${big_padding ? "yes" : "no"}`, `background-wrapper--big-padding-${big_padding ? "yes" : "no"}`,
{ {
"background-wrapper--grow-height": grow_to_screen_height, "background-wrapper--grow-height": grow_to_screen_height,
"background-wrapper--crooked": crooked,
"jdd-no-gap": !margin, "jdd-no-gap": !margin,
}, },
...classes, ...classes,

View File

@ -25,7 +25,51 @@
gap: 24px; gap: 24px;
} }
&.buttons--bleed-none {
grid-column: content !important;
}
&.buttons--bleed-bleed {
grid-column: bleed !important;
}
&.buttons--bleed-screen {
grid-column: 1/-1 !important;
}
.button { .button {
margin: 0; margin: 0;
&.button--size-tiny {
font-size: 10px;
}
&.button--size-small {
font-size: 14px;
}
&.button--size-medium {
font-size: 16px;
@container (max-width: 500px) {
font-size: 15px;
}
}
&.button--size-large {
font-size: 24px;
@container (max-width: 500px) {
font-size: 18px;
}
}
&.button--size-extra-large {
font-size: 32px;
@container (max-width: 500px) {
font-size: 24px;
}
}
} }
} }

View File

@ -6,11 +6,21 @@ import { button, button_variants } from "src/back/elements/button.js";
const component_arguments = { const component_arguments = {
mode: new ComponentArguments.Enum(["column", "row"]), mode: new ComponentArguments.Enum(["column", "row"]),
gap: new ComponentArguments.Enum(["none", "narrow", "medium", "wide"]), gap: new ComponentArguments.Enum(["none", "narrow", "medium", "wide"]),
bleed: new ComponentArguments.Enum(["none", "bleed", "screen"]).setDefaultValue(
"none"
),
buttons: new ComponentArguments.List( buttons: new ComponentArguments.List(
new ComponentArguments.Structured({ new ComponentArguments.Structured({
variant: new ComponentArguments.Enum(button_variants), variant: new ComponentArguments.Enum(button_variants),
text: new ComponentArguments.ShortText(), text: new ComponentArguments.ShortText(),
href: new ComponentArguments.ShortText(), href: new ComponentArguments.ShortText(),
size: new ComponentArguments.Enum([
"tiny",
"small",
"medium",
"large",
"extra-large",
]).setDefaultValue("medium"),
}) })
), ),
} as const; } as const;
@ -21,7 +31,7 @@ export class Buttons extends Component<typeof component_arguments> {
} }
async toHTML({ async toHTML({
args: { buttons, mode, gap }, args: { buttons, mode, gap, bleed },
classes, classes,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> { }: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
return ( return (
@ -30,10 +40,17 @@ export class Buttons extends Component<typeof component_arguments> {
"buttons", "buttons",
`buttons--mode-${mode}`, `buttons--mode-${mode}`,
`buttons--gap-${gap}`, `buttons--gap-${gap}`,
`buttons--bleed-${bleed}`,
...classes, ...classes,
]} ]}
> >
{buttons.map((b) => button({ ...b, text: insert_nbsp(b.text) }))} {buttons.map((b) =>
button({
...b,
text: insert_nbsp(b.text),
classes: [`button--size-${b.size}`],
})
)}
</div> </div>
); );
} }

View File

@ -0,0 +1,272 @@
.dynamic-grid {
container-type: inline-size;
&.dynamic-grid--bleed-bleed {
grid-column: bleed !important;
}
&.dynamic-grid--bleed-screen {
grid-column: screen !important;
}
.dynamic-grid-title {
text-align: center;
margin-top: 0;
}
.tiles-container {
display: grid;
grid-gap: 24px;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: min-content;
grid-auto-flow: row dense;
@container (min-width: 900px) {
grid-template-columns: repeat(4, 1fr);
}
.tile {
overflow: hidden;
display: grid;
gap: 0;
grid-template-columns: subgrid;
grid-template-rows: subgrid;
color: var(--color-brand-text-fg);
text-decoration: none;
.tile-content {
grid-row: 2/3;
background-color: var(--tile-bg-color);
color: var(--tile-fg-color);
width: 100%;
padding: 16px;
z-index: 1;
overflow: hidden;
bottom: 0;
box-sizing: border-box;
.tile-content-wrapper {
display: flex;
flex-flow: column;
gap: 16px;
.tile-title {
font-weight: bold;
line-height: 20px;
margin: 0;
}
.tile-subtitle {
line-height: 20px;
margin: 0;
}
}
}
.tile-image {
z-index: 0;
width: 100%;
height: 100%;
grid-row: 1/2;
picture {
height: 100%;
width: 100% !important;
}
}
}
.tile.square {
grid-column: span 1;
grid-row: span 2;
.tile-content {
grid-row: 2/3;
}
.tile-image--horizontal {
display: none;
}
.tile-image--vertical {
display: none;
}
.tile-image--square {
grid-row: 1/2;
picture img {
display: block !important;
}
}
}
.tile.horizontal {
grid-column: span 2;
grid-row: span 2;
.tile-content {
grid-column: 1/3;
}
.tile-image--vertical {
display: none;
}
.tile-image--square {
display: none;
}
.tile-image--horizontal {
grid-column: 1/3;
picture img {
display: block !important;
}
}
}
.tile.vertical {
grid-row: span 4;
grid-column: span 1;
.tile-content {
grid-row: 4/5;
}
.tile-image--horizontal {
display: none;
}
.tile-image--square {
display: none;
}
.tile-image--vertical {
grid-row: 1/4;
picture img {
display: block !important;
}
}
}
.buttons-container {
margin-top: 36px;
display: flex;
justify-content: center;
flex-flow: row wrap;
column-gap: 18px;
row-gap: 16px;
.button {
display: inline-block;
text-align: center;
padding: 8px 24px;
border: 1px solid var(--color-brand-text-fg);
color: var(--text-color);
min-width: 240px;
line-height: 23px;
font-size: 16px;
&.accent {
background-color: var(--color-brand-accent);
--text-color: var(--color-brand-text-on-accent);
}
&.accent2 {
background-color: var(--color-brand-accent2);
--text-color: var(--color-brand-text-on-accent2);
}
}
}
}
@container (max-width: 900px) {
.tiles-container {
grid-template-columns: repeat(3, 1fr);
}
}
@container (max-width: 700px) {
.tiles-container {
grid-template-columns: repeat(2, 1fr);
.tile.vertical {
grid-row: span 2;
grid-column: span 1;
.tile-image--vertical {
display: none;
}
.tile-image--horizontal {
display: none;
}
.tile-image--square {
display: block;
picture img {
display: block !important;
}
}
}
}
}
@container (max-width: 500px) {
.tiles-container {
grid-template-columns: 1fr;
max-width: 700px;
margin: 0 auto;
.tile {
grid-column: span 1 !important;
grid-row: span 2 !important;
.tile-content {
grid-row: 2/3 !important;
}
.tile-image--vertical {
display: none !important;
}
.tile-image--square {
display: none !important;
}
.tile-image--horizontal {
display: block !important;
grid-row: 1/2 !important;
picture img {
display: block !important;
}
}
}
}
}
&.dynamic-grid--style-pop-out {
.tile {
overflow: visible;
.tile-content {
transform: translateY(-20px) translateX(20px);
box-shadow: -10px 10px 0px #00000026;
}
.tile-image {
grid-row: 1/3 !important;
}
.tile-image--vertical {
grid-row: 1/5 !important;
}
}
}
}

View File

@ -0,0 +1,119 @@
import type { FlatTemplatable } from "tempstream";
import { TempstreamJSX } from "tempstream";
import type { ComponentToHTMLArgs, ExtractParsed, JDDContext } from "@sealcode/jdd";
import { Component, ComponentArguments, insert_nbsp } from "@sealcode/jdd";
import { getFgColorVariable } from "src/back/colors.js";
type ExtractArray<T> = T extends Array<infer X> ? X : never;
const component_arguments = {
bleed: new ComponentArguments.Enum(["none", "bleed", "screen"]).setDefaultValue(
"none"
),
style: new ComponentArguments.Enum(["flat", "pop-out"]),
tiles: new ComponentArguments.List(
new ComponentArguments.Structured({
title: new ComponentArguments.ShortText(),
subtitle: new ComponentArguments.ShortText(),
url: new ComponentArguments.ShortText().setDefaultValue(""),
photo: new ComponentArguments.Structured({
image: new ComponentArguments.Image(),
alt: new ComponentArguments.ShortText(),
}),
shape: new ComponentArguments.Enum([
"square",
"horizontal",
"vertical",
]).setDefaultValue("square"),
background: new ComponentArguments.Enum([
"accent",
"accent2",
"text-bg",
]).setDefaultValue("accent"),
})
),
} as const;
export class DynamicGridV2 extends Component<typeof component_arguments> {
getArguments() {
return component_arguments;
}
public image_sizes = {
square: { width: 700, height: 700 },
horizontal: { width: 824, height: 400 },
vertical: { width: 400, height: 824 },
};
renderTile(
jdd_context: JDDContext,
tile: ExtractArray<ExtractParsed<typeof component_arguments.tiles>>
) {
return (
<a
href={tile.url || false}
class={["tile", tile.shape]}
style={[
`--tile-bg-color: var(--color-brand-${tile.background})`,
`--tile-fg-color: var(${getFgColorVariable("brand-" + tile.background)})`,
].join(";")}
>
<div class="tile-content">
<div class="tile-content-wrapper">
{tile.title ? (
<h3 class="tile-title">{insert_nbsp(tile.title)}</h3>
) : (
""
)}
{tile.subtitle ? (
<p class="tile-subtitle">{insert_nbsp(tile.subtitle)}</p>
) : (
""
)}
</div>
</div>
{(["square", "horizontal", "vertical"] as const).map((shape) => (
<div class={["tile-image", `tile-image--${shape}`]}>
{!tile.photo
? ""
: jdd_context.render_image(tile.photo.image, {
sizesAttr:
"(max-width: 730px) 100vw, (max-width: 980px) 50vw, 810px",
alt: tile.photo.alt,
container: {
...this.image_sizes[shape],
objectFit: "cover",
},
crop: this.image_sizes[shape],
imgStyle: "display: none;",
})}
</div>
))}
</a>
);
}
toHTML({
args: { tiles, bleed, style },
jdd_context,
classes,
index,
}: ComponentToHTMLArgs<typeof component_arguments>): FlatTemplatable {
return (
<div
class={[
"dynamic-grid",
`dynamic-grid--bleed-${bleed}`,
`dynamic-grid--style-${style}`,
...classes,
]}
style={`--jdd-index: ${index}`}
>
<div class="tiles-container">
{tiles.map((tile) => this.renderTile(jdd_context, tile))}
</div>
</div>
);
}
}

View File

@ -139,13 +139,11 @@
} }
.question { .question {
font-size: 14px;
line-height: 21px; line-height: 21px;
padding: 10px 0; padding: 10px 0;
} }
.answer p { .answer p {
font-size: 14px;
line-height: 21px; line-height: 21px;
} }

View File

@ -41,7 +41,7 @@ export class FaqComponent extends Component<typeof component_arguments> {
<div class={["faq-component", ...classes]}> <div class={["faq-component", ...classes]}>
<div class="container"> <div class="container">
<div class="title-container"> <div class="title-container">
<div class="title">{title} </div> {title ? <div class="title">{title} </div> : ""}
<div class={["content", `color-${content.color}`]}> <div class={["content", `color-${content.color}`]}>
{render_markdown(language, content.text)}{" "} {render_markdown(language, content.text)}{" "}
</div> </div>

View File

@ -1,4 +1,7 @@
.heading { .heading {
text-wrap: pretty;
text-wrap: balance;
& > * { & > * {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
@ -10,18 +13,46 @@
&.heading--font-size-small > * { &.heading--font-size-small > * {
font-size: 24px; font-size: 24px;
@container (max-width: 600px) {
font-size: 22px;
}
} }
&.heading--font-size-medium > * { &.heading--font-size-medium > * {
font-size: 32px; font-size: 32px;
@container (max-width: 600px) {
font-size: 24px;
}
} }
&.heading--font-size-large > * { &.heading--font-size-large > * {
font-size: 48px; font-size: 48px;
@container (max-width: 600px) {
font-size: 32px;
}
} }
&.heading--font-size-extra_large > * { &.heading--font-size-extra_large > * {
font-size: 64px; font-size: 64px;
@container (max-width: 600px) {
font-size: 56px;
}
}
&.heading--align-left {
text-align: left;
}
&.heading--align-right {
text-align: right;
}
&.heading--align-center {
text-align: center;
} }
&.heading--color-normal { &.heading--color-normal {

View File

@ -18,6 +18,9 @@ const component_arguments = {
"large", "large",
"extra_large", "extra_large",
]).setDefaultValue("medium"), ]).setDefaultValue("medium"),
align: new ComponentArguments.Enum(["left", "right", "center"]).setDefaultValue(
"left"
),
color: new ComponentArguments.Enum(["normal", "accent1", "accent2"]).setDefaultValue( color: new ComponentArguments.Enum(["normal", "accent1", "accent2"]).setDefaultValue(
"normal" "normal"
), ),
@ -30,7 +33,7 @@ export class Heading extends Component<typeof component_arguments> {
} }
async toHTML({ async toHTML({
args: { title, level, font_size, color }, args: { title, level, font_size, color, align },
classes, classes,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> { }: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
return ( return (
@ -39,6 +42,7 @@ export class Heading extends Component<typeof component_arguments> {
"heading", "heading",
`heading--font-size-${font_size}`, `heading--font-size-${font_size}`,
`heading--color-${color}`, `heading--color-${color}`,
`heading--align-${align}`,
...classes, ...classes,
]} ]}
> >

View File

@ -27,9 +27,9 @@
background-color: var(--tile-bg-color); background-color: var(--tile-bg-color);
display: grid; display: grid;
grid-template-rows: subgrid; grid-template-rows: subgrid;
grid-row: span 2; grid-row: span 4;
flex-flow: column; flex-flow: column;
gap: 32px; --gap: 16px;
min-height: 320px; min-height: 320px;
max-width: 400px; max-width: 400px;
text-decoration: none; /* in case it's an a href */ text-decoration: none; /* in case it's an a href */
@ -39,13 +39,8 @@
color: var(--tile-fg-color); color: var(--tile-fg-color);
} }
&:has(.icon), & > * + * {
&:has(.icon-tiles__tile__cta) { margin-top: var(--gap);
grid-row: span 3;
}
&:has(.icon):has(.icon-tiles__tile__cta) {
grid-row: span 4;
} }
svg { svg {
@ -80,7 +75,10 @@
&:hover { &:hover {
transform: scale(1.05); transform: scale(1.05);
}
&:hover,
&.icon-tiles-grid__tile--force-cta {
.icon-tiles__tile__cta { .icon-tiles__tile__cta {
text-align: right; text-align: right;
opacity: 1; opacity: 1;

View File

@ -23,6 +23,7 @@ const component_arguments = {
description: new Args.ShortText(), description: new Args.ShortText(),
url: new Args.ShortText().setExampleValues([""]), url: new Args.ShortText().setExampleValues([""]),
cta: new Args.ShortText().setExampleValues([""]), cta: new Args.ShortText().setExampleValues([""]),
always_show_cta: new Args.Boolean().setDefaultValue(false),
}) })
), ),
} as const; } as const;
@ -42,6 +43,7 @@ export class IconTilesGrid extends Component<typeof component_arguments> {
class={[ class={[
"icon-tiles-grid", "icon-tiles-grid",
{ "icon-tiles-grid--bleed": bleed }, { "icon-tiles-grid--bleed": bleed },
...classes, ...classes,
]} ]}
> >
@ -50,7 +52,13 @@ export class IconTilesGrid extends Component<typeof component_arguments> {
<section class="icon-tiles-grid__grid"> <section class="icon-tiles-grid__grid">
{tiles.map((tile) => ( {tiles.map((tile) => (
<a <a
class="icon-tiles-grid__tile" class={[
"icon-tiles-grid__tile",
{
"icon-tiles-grid__tile--force-cta":
tile.always_show_cta,
},
]}
style={`--tile-bg-color: var(${tile.color}); --tile-fg-color: var(${getFgColorVariable(tile.color, 4.2)})`} style={`--tile-bg-color: var(${tile.color}); --tile-fg-color: var(${getFgColorVariable(tile.color, 4.2)})`}
href={tile.url ? tile.url : false} href={tile.url ? tile.url : false}
> >

View File

@ -3,7 +3,7 @@
display: grid; display: grid;
grid-template-columns: subgrid; grid-template-columns: subgrid;
grid-column: screen !important; grid-column: 1/-1 !important;
.split-view__half { .split-view__half {
flex-basis: 50%; flex-basis: 50%;

View File

@ -104,7 +104,7 @@
.horizontal-scroller__element { .horizontal-scroller__element {
width: var(--horizontal-scroller-item-width) !important; width: var(--horizontal-scroller-item-width) !important;
max-width: calc(100cqw - 80px); max-width: calc(100cqw - 110px);
flex-shrink: 0; flex-shrink: 0;
container-type: inline-size; container-type: inline-size;
flex-flow: column; flex-flow: column;
@ -115,3 +115,13 @@
} }
} }
} }
.grid-layout-main > .subtree,
.subgrid-layout > .subtree {
display: grid !important;
grid-template-columns: subgrid;
& > * {
grid-column: content;
}
}

View File

@ -11,6 +11,14 @@
font-size: 20px; font-size: 20px;
} }
&.tekst--bleed-none {
grid-column: content !important;
}
&.tekst--bleed-bleed {
grid-column: bleed !important;
}
p { p {
margin: 0; margin: 0;
text-align: justify; text-align: justify;

View File

@ -17,6 +17,7 @@ const component_arguments = {
size: new ComponentArguments.Enum(["small", "normal", "large"]).setDefaultValue( size: new ComponentArguments.Enum(["small", "normal", "large"]).setDefaultValue(
"normal" "normal"
), ),
bleed: new ComponentArguments.Enum(["none", "bleed"]).setDefaultValue("none"),
content: new ComponentArguments.Markdown(), content: new ComponentArguments.Markdown(),
side_content: new ComponentArguments.NestedComponent(), side_content: new ComponentArguments.NestedComponent(),
} as const; } as const;
@ -42,7 +43,7 @@ export class Tekst extends Component<typeof component_arguments> {
} }
toHTML({ toHTML({
args: { color, content, side_content, size }, args: { color, content, side_content, size, bleed },
classes, classes,
jdd_context, jdd_context,
index, index,
@ -55,6 +56,7 @@ export class Tekst extends Component<typeof component_arguments> {
...classes, ...classes,
`tekst--color-${color}`, `tekst--color-${color}`,
`tekst--size-${size}`, `tekst--size-${size}`,
`tekst--bleed-${bleed}`,
]} ]}
style={`--jdd-index: ${index}`} style={`--jdd-index: ${index}`}
> >

View File

@ -51,11 +51,15 @@ footer {
.footer__social_media_icons { .footer__social_media_icons {
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
flex-wrap: row balance;
flex-grow: 1; flex-grow: 1;
justify-content: space-between; justify-content: space-between;
row-gap: 16px;
column-gap: 8px;
a { a {
padding: 2px 8px; padding: 2px 8px;
display: inline-flex;
svg { svg {
width: 24px; width: 24px;
@ -66,6 +70,18 @@ footer {
} }
} }
} }
@media (max-width: 800px) {
display: block;
text-align: center;
text-wrap: balance;
line-height: 40px;
& > a {
display: inline;
text-decoration: none;
}
}
} }
.footer__attribution { .footer__attribution {

60
src/icons/mobilizon.svg Normal file
View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32mm"
height="32mm"
viewBox="0 0 32 32"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="mobilizon.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="3.4707207"
inkscape:cx="30.397145"
inkscape:cy="42.210253"
inkscape:window-width="1920"
inkscape:window-height="1048"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs1"><style
id="style1">.a{fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;}</style></defs><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-177.53542,-119.0625)"><g
id="g1"
transform="matrix(0.75548084,0,0,0.75548084,175.40374,116.87053)"><path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;stroke-linecap:round;stroke-linejoin:round;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 23.931641,4.0800781 c -1.494748,0.025421 -2.52847,0.1192604 -3.265625,0.7089844 -0.368578,0.294862 -0.619956,0.7111696 -0.759766,1.1992187 -0.139345,0.4864262 -0.185233,1.0523881 -0.185547,1.7363282 -0.03037,1.4924624 0.259457,2.5642576 1.052734,3.2070316 0.794471,0.643741 1.91084,0.779296 3.386719,0.779297 1.551245,0.005 2.60229,-0.147529 3.291016,-0.837891 0.689065,-0.690702 0.828282,-1.7236503 0.86914,-3.1894533 C 28.357005,6.4150902 28.147315,5.4229933 27.390625,4.8125 26.633935,4.2020067 25.53208,4.0595847 23.933594,4.0800781 c -6.67e-4,-1.3e-6 -0.0013,-1.3e-6 -0.002,0 z"
id="path1"
sodipodi:nodetypes="csscscscsccc" /><path
class="a"
d="M 36,29.16 C 36.05,37.82 31.17,43.7 23.82,43.58 17.29,43.49 12,38.58 12,29.16 12,19.74 16.46,15.16 23.94,14.87 31.42,14.58 36,20.15 36,29.16 Z"
id="path2" /><path
class="a"
d="m 29.28,29.29 c 0,6 -1.49,9.31 -5.4,9.29 -3.49,0 -5.11,-3.4 -5.13,-9.28 0,-6.18 2,-9.19 5.23,-9.29 3,-0.1 5.35,2.74 5.31,9.28 z"
id="path3" /></g><path
style="fill:#000000;fill-opacity:1;stroke-width:1.63075;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:17.9383;paint-order:stroke fill markers"
d="m 190.17388,149.106 c -3.41643,-0.61284 -4.51451,-3.42982 -5.30886,-7.23609 -0.24855,-1.19096 -0.51369,-1.95879 -0.47777,-3.59824 0.0348,-1.58671 0.48245,-2.48533 0.79336,-3.63603 0.87994,-3.25672 3.68399,-5.96402 6.93009,-6.51897 3.03375,-0.51865 5.69992,0.75889 7.46745,2.54583 1.42814,1.44384 2.68435,3.71352 2.98912,6.31885 0.10801,0.92337 -0.0557,4.52261 -0.15055,5.39307 -0.44331,4.06978 -3.44298,5.8085 -6.61452,6.80223 -0.80664,0.25275 -1.31459,0.35794 -2.32135,0.35174 -0.50058,-0.003 -1.84357,-0.14447 -2.11835,-0.19377 z m 4.34963,-3.02605 c 1.56873,-0.36986 2.58576,-1.67689 3.00803,-3.86579 0.2998,-1.55404 0.33762,-4.45171 0.0771,-5.9072 -0.52058,-2.90843 -1.93095,-4.49937 -3.98526,-4.4955 -2.78074,0.005 -4.33076,2.57571 -4.3301,7.18086 5.9e-4,4.09547 1.09392,6.5186 3.19664,7.08457 0.43769,0.11782 1.53984,0.11947 2.03359,0.003 z"
id="path4"
sodipodi:nodetypes="cscsscsccccccsscsccc" /><path
style="fill:#ffffff;stroke-width:6.35582;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:69.914;paint-order:stroke fill markers"
d=""
id="path5"
transform="matrix(0.26458333,0,0,0.26458333,177.53542,119.0625)" /></g></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB