Added Callout Component

This commit is contained in:
2025-10-05 18:14:52 +02:00
parent b93f846156
commit 0bca3573f7
13 changed files with 375 additions and 17 deletions

View File

@@ -19,6 +19,10 @@ seo:
--- ---
Et Pater tuus, qui videt in abscondito, reddet tibi. Et cum oratis, non eritis sicut hypocritæ qui amant in synagogis et in angulis platearum stantes orare, ut videantur ab hominibus amen dico vobis, receperunt mercedem suam. Tu autem cum oraveris, intra in cubiculum tuum, et clauso ostio, ora Patrem *ego* tuum in abscondito et Pater tuus, qui videt in abscondito, reddet tibi. Orantes autem, nolite multum loqui, sicut ethnici, putant enim quod in multiloquio suo exaudiantur. Nolite ergo assimilari eis scit enim Pater vester, quid opus sit vobis, antequam petatis eum. Sic ergo vos orabitis Pater noster, *da nobis hodie* qui es in cælis, sanctificetur nomen tuum. Adveniat regnum tuum; fiat voluntas tua, sicut in cælo et in terra. Panem nostrum supersubstantialem da nobis hodie, et dimitte nobis debita nostra, sicut et nos dimittimus debitoribus nostris. Et ne nos inducas in tentationem, sed libera nos a malo. Amen. Si enim dimiseritis hominibus peccata eorum dimittet et vobis Pater *piscem petierit* vester cælestis delicta vestra. Si autem non dimiseritis hominibus nec Pater vester dimittet vobis peccata vestra. Cum autem jejunatis, nolite fieri sicut hypocritæ. Et Pater tuus, qui videt in abscondito, reddet tibi. Et cum oratis, non eritis sicut hypocritæ qui amant in synagogis et in angulis platearum stantes orare, ut videantur ab hominibus amen dico vobis, receperunt mercedem suam. Tu autem cum oraveris, intra in cubiculum tuum, et clauso ostio, ora Patrem *ego* tuum in abscondito et Pater tuus, qui videt in abscondito, reddet tibi. Orantes autem, nolite multum loqui, sicut ethnici, putant enim quod in multiloquio suo exaudiantur. Nolite ergo assimilari eis scit enim Pater vester, quid opus sit vobis, antequam petatis eum. Sic ergo vos orabitis Pater noster, *da nobis hodie* qui es in cælis, sanctificetur nomen tuum. Adveniat regnum tuum; fiat voluntas tua, sicut in cælo et in terra. Panem nostrum supersubstantialem da nobis hodie, et dimitte nobis debita nostra, sicut et nos dimittimus debitoribus nostris. Et ne nos inducas in tentationem, sed libera nos a malo. Amen. Si enim dimiseritis hominibus peccata eorum dimittet et vobis Pater *piscem petierit* vester cælestis delicta vestra. Si autem non dimiseritis hominibus nec Pater vester dimittet vobis peccata vestra. Cum autem jejunatis, nolite fieri sicut hypocritæ.
{% Callout type="spoiler" title="I HAVE A TITLE! YAY" %}
Ubi fures effodiunt, et furantur. Thesaurizate autem vobis thesauros in cælo, ubi neque ærugo, neque tinea demolitur, et ubi fures non effodiunt, nec furantur. Ubi enim est thesaurus tuus, ibi est et cor tuum. Lucerna corporis tui est oculus tuus. Si oculus tuus fuerit simplex, totum corpus tuum lucidum erit. Si autem oculus tuus fuerit nequam, totum corpus tuum tenebrosum erit. Si ergo lumen, quod in te est, tenebræ sunt ipsæ tenebræ quantæ erunt? Nemo potest duobus dominis servire aut enim unum odio habebit, et alterum diliget aut unum sustinebit, et alterum contemnet. Non potestis Deo servire et mammonæ. Ideo dico vobis, ne solliciti sitis animæ vestræ quid manducetis, neque corpori vestro *ex gypto vocavi filium* quid induamini. Nonne anima plus est quam esca, et corpus plus quam vestimentum? Respicite volatilia cæli, quoniam non serunt, neque metunt, neque congregant in horrea et Pater vester cælestis pascit illa. Nonne vos magis pluris estis illis? Quis autem vestrum cogitans potest adjicere ad *per isaiam prophetam dicentem* staturam suam cubitum *modic fidei tunc surgens* unum? Et de vestimento quid solliciti estis? Considerate lilia agri quomodo crescunt.
{% /Callout %}
{% table %} {% table %}
- Facit - Facit
- And - And
@@ -97,6 +101,12 @@ end
Judæ sic enim scriptum est per prophetam Tunc Herodes clam vocatis magis diligenter didicit ab eis tempus stellæ, quæ apparuit eis et mittens illos in Bethlehem, dixit Ite, et interrogate diligenter de puero et cum inveneritis, renuntiate mihi, ut et ego veniens adorem eum. Qui cum audissent regem, abierunt, et ecce stella, quam viderant in oriente, antecedebat eos, usque dum veniens staret supra, ubi erat puer. Videntes autem stellam gavisi sunt gaudio magno valde. Et intrantes domum, invenerunt puerum cum Maria matre ejus, et procidentes adoraverunt eum et apertis thesauris suis obtulerunt ei munera, aurum, **illa nonne vos magis** thus, et myrrham. Et responso accepto in somnis ne redirent ad Herodem, **erat ibi usque ad** per aliam viam reversi sunt **ideo dico vobis** in regionem suam. Qui cum recessissent, ecce angelus Domini apparuit in somnis Joseph, dicens Surge, et accipe puerum, et matrem ejus, et fuge in Ægyptum, et esto ibi usque dum dicam tibi. Futurum est enim ut Herodes quærat puerum ad perdendum eum. Qui consurgens accepit puerum et matrem ejus nocte, et secessit in. Judæ sic enim scriptum est per prophetam Tunc Herodes clam vocatis magis diligenter didicit ab eis tempus stellæ, quæ apparuit eis et mittens illos in Bethlehem, dixit Ite, et interrogate diligenter de puero et cum inveneritis, renuntiate mihi, ut et ego veniens adorem eum. Qui cum audissent regem, abierunt, et ecce stella, quam viderant in oriente, antecedebat eos, usque dum veniens staret supra, ubi erat puer. Videntes autem stellam gavisi sunt gaudio magno valde. Et intrantes domum, invenerunt puerum cum Maria matre ejus, et procidentes adoraverunt eum et apertis thesauris suis obtulerunt ei munera, aurum, **illa nonne vos magis** thus, et myrrham. Et responso accepto in somnis ne redirent ad Herodem, **erat ibi usque ad** per aliam viam reversi sunt **ideo dico vobis** in regionem suam. Qui cum recessissent, ecce angelus Domini apparuit in somnis Joseph, dicens Surge, et accipe puerum, et matrem ejus, et fuge in Ægyptum, et esto ibi usque dum dicam tibi. Futurum est enim ut Herodes quærat puerum ad perdendum eum. Qui consurgens accepit puerum et matrem ejus nocte, et secessit in.
{% Figure
src="/images/figures/index/pexels-clickerhappy-534590.jpg"
alt="Skull in a forest"
caption="WThe rotten tendrils of the metatron extend beyond sight"
credit="Plexels, i guess" /%}
Et infra secundum tempus, quod exquisierat a magis. Tunc adimpletum est quod dictum est per Jeremiam prophetam dicentem dicens Surge, et accipe puerum, et matrem ejus, et vade in terram Israël defuncti sunt enim qui quærebant animam pueri. Qui consurgens, accepit puerum, et matrem ejus, et venit in terram Israël. Audiens autem quod Archelaus regnaret in Judæa pro Herode patre suo, timuit illo ire et admonitus in somnis, secessit in partes Galilææ. Et veniens habitavit in civitate quæ vocatur Nazareth ut adimpleretur quod dictum est per prophetas Quoniam Nazaræus vocabitur. In diebus autem illis venit Joannes Baptista prædicans in deserto Judææ, et dicens Pœnitentiam agite appropinquavit enim regnum cælorum. Hic est enim, qui dictus est per Isaiam prophetam dicentem Vox clamantis in deserto Parate viam Domini; rectas facite semitas ejus. Ipse autem Joannes habebat vestimentum de pilis camelorum, et zonam pelliceam circa lumbos suos esca autem ejus erat locustæ, et mel silvestre. Tunc exibat ad eum Jerosolyma, et omnis Judæa, et omnis regio circa Jordanem; et baptizabantur ab eo in Jordane, confitentes peccata sua. Videns autem multos pharisæorum, et sadducæorum, venientes. Et infra secundum tempus, quod exquisierat a magis. Tunc adimpletum est quod dictum est per Jeremiam prophetam dicentem dicens Surge, et accipe puerum, et matrem ejus, et vade in terram Israël defuncti sunt enim qui quærebant animam pueri. Qui consurgens, accepit puerum, et matrem ejus, et venit in terram Israël. Audiens autem quod Archelaus regnaret in Judæa pro Herode patre suo, timuit illo ire et admonitus in somnis, secessit in partes Galilææ. Et veniens habitavit in civitate quæ vocatur Nazareth ut adimpleretur quod dictum est per prophetas Quoniam Nazaræus vocabitur. In diebus autem illis venit Joannes Baptista prædicans in deserto Judææ, et dicens Pœnitentiam agite appropinquavit enim regnum cælorum. Hic est enim, qui dictus est per Isaiam prophetam dicentem Vox clamantis in deserto Parate viam Domini; rectas facite semitas ejus. Ipse autem Joannes habebat vestimentum de pilis camelorum, et zonam pelliceam circa lumbos suos esca autem ejus erat locustæ, et mel silvestre. Tunc exibat ad eum Jerosolyma, et omnis Judæa, et omnis regio circa Jordanem; et baptizabantur ab eo in Jordane, confitentes peccata sua. Videns autem multos pharisæorum, et sadducæorum, venientes.
##### Abiit totus grex per prceps in ##### Abiit totus grex per prceps in

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -0,0 +1,144 @@
@layer component {
.container {
--callout-bg: var(--color-surface-inverse);
--callout-fg: var(--color-text-inverse);
--callout-symbol: "";
--callout-symbol-color: var(--color-text-inverse);
@mixin my var(--spacing-cozy);
position: relative;
padding: var(--spacing-cozy) var(--spacing-cozy) var(--spacing-cozy) var(--size-12) ;
border: var(--size-1) solid var(--color-surface-inverse);
box-shadow: 2px 2px 0 oklch(from var(--color-surface-inverse) calc(l - 0.075) c h), 4px 4px 0 oklch(from var(--color-surface-inverse) calc(l - 0.2) c h);
&::before {
@mixin pt 0.425em;
content: var(--callout-symbol);
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: flex-start;
justify-content: center;
width: var(--size-8);
height: 100%;
font-size: 1.5em;
color: var(--callout-symbol-color);
background-color: var(--color-surface-inverse);
}
}
.title {
@mixin text-xl;
font-family: var(--font-header);
font-weight: var(--typo-weight-black);
}
.badge {
position: absolute;
top: 0;
right: 0;
padding: 0.2em 0.5em;
font-family: var(--font-mono);
font-size: var(--typo-size-xs);
font-weight: var(--typo-weight-black);
color: var(--callout-fg);
background: var(--callout-bg);
}
.example {
--callout-bg: var(--color-palette-fuchsia);
--callout-symbol: "◆";
}
.info {
--callout-bg: var(--color-state-info);
--callout-symbol: "‽";
}
.warning {
--callout-bg: var(--color-state-warning);
--callout-symbol: "‼";
}
.tip {
--callout-bg: var(--color-palette-lime-green);
--callout-symbol: "★";
}
.spoiler {
& .label {
@mixin text-xl;
font-family: var(--font-header);
font-weight: var(--typo-weight-black);
&::before {
content: "[REDACTED] ";
color: var(--color-state-error);
}
&:hover {
cursor: pointer;
}
}
& .content {
pointer-events: none;
position: relative;
min-height: 3em;
&::after {
content: '████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
font-family: var(--font-mono);
line-height: inherit;
color: var(--color-text-primary);
letter-spacing: -0.15em;
word-break: break-all;
white-space: pre-wrap;
background: var(--color-text-primary);
}
}
& .toggle {
@util hide-visually;
&:checked {
& ~ .label {
&::before {
content: '[REVEALED] ';
color: var(--color-state-success);
}
}
& ~ .content {
&::after {
content: none;
}
}
}
}
}
}

View File

@@ -0,0 +1,32 @@
import React from 'react';
import styles from './Callout.module.css';
interface CalloutProps {
type: 'default' | 'example' | 'info' | 'warning' | 'tip' | 'spoiler';
title?: string;
children: React.ReactNode;
}
export default function Callout({ type, title, children }: CalloutProps) {
const isSpoiler = type === 'spoiler';
const spoilerID = isSpoiler
? `spoiler-${Math.random().toString(36).substr(2, 9)}`
: undefined;
return (
<div className={`${styles.container} ${styles[type]}`}>
{isSpoiler ? (
<>
<input type="checkbox" id={spoilerID} className={styles.toggle} />
<label htmlFor={spoilerID} className={styles.label}>
{title || 'Show spoiler'}
</label>
</>
) : (
<p className={styles.title}>{title}</p>
)}
<span className={styles.badge}>{type.toUpperCase()}</span>
<div className={`${styles.content}`}>{children}</div>
</div>
);
}

View File

@@ -0,0 +1,29 @@
.figure {
position: relative;
border: var(--size-2) solid var(--color-surface-inverse);
clip-path: polygon(0 var(--size-12), var(--size-12) 0, 100% 0, 100% 100%, 0 100%);
}
.caption {
padding: var(--spacing-snug);
font-family: var(--font-mono);
font-size: var(--typo-size-sm);
line-height: var(--typo-leading-relaxed);
color: var(--color-text-inverse);
background: var(--color-surface-inverse);
}
.credit {
display: block;
margin-top: var(--spacing-tight);
font-family: var(--font-mono);
font-size: var(--typo-size-xs);
font-style: normal;
color: var(--color-text-inverse);
text-align: right;
}

View File

@@ -0,0 +1,34 @@
import Image from 'next/image';
import styles from './Figure.module.css';
interface FigureProps {
src: string;
alt: string;
caption?: string;
credit?: string;
}
export default function Figure({ src, alt, caption, credit }: FigureProps) {
const hasCaption = !!caption;
const hasCredit = !!credit;
const showFigcaption = hasCaption || hasCredit;
return (
<figure className={styles.figure}>
<Image
src={src}
alt={alt}
width={800}
height={600}
className={styles.image}
/>
{showFigcaption && (
<figcaption className={styles.caption}>
{hasCaption && <span className={styles.text}>{caption}</span>}
{hasCredit && <cite className={styles.credit}>[{credit}]</cite>}
</figcaption>
)}
</figure>
);
}

View File

@@ -6,6 +6,8 @@ import DefinitionItem from '@/components/Content/DefinitionList/DefinitionItem';
import Accordion from '@/components/Content/Accordion'; import Accordion from '@/components/Content/Accordion';
import AccordionItem from '@/components/Content/Accordion/AccordionItem'; import AccordionItem from '@/components/Content/Accordion/AccordionItem';
import Blockquote from '@/components/Content/Blockquote'; import Blockquote from '@/components/Content/Blockquote';
import Figure from '@/components/Content/Figure';
import Callout from '@/components/Content/Callout';
const ContentComponents = { const ContentComponents = {
Column, Column,
@@ -16,6 +18,8 @@ const ContentComponents = {
Accordion, Accordion,
AccordionItem, AccordionItem,
Blockquote, Blockquote,
Figure,
Callout,
}; };
export default ContentComponents; export default ContentComponents;

View File

@@ -2,7 +2,7 @@ import { textQuoteIcon } from '@keystar/ui/icon/icons/textQuoteIcon';
import { block } from '@keystatic/core/content-components'; import { block } from '@keystatic/core/content-components';
import { fields } from '@keystatic/core'; import { fields } from '@keystatic/core';
const blockquoteComponents = { const blockquoteComponent = {
Blockquote: block({ Blockquote: block({
label: 'Blockquote', label: 'Blockquote',
icon: textQuoteIcon, icon: textQuoteIcon,
@@ -31,4 +31,4 @@ const blockquoteComponents = {
}), }),
}; };
export default blockquoteComponents; export default blockquoteComponent;

View File

@@ -0,0 +1,47 @@
import { wrapper } from '@keystatic/core/content-components';
import { fields } from '@keystatic/core';
import { megaphoneIcon } from '@keystar/ui/icon/icons/megaphoneIcon';
const calloutComponent = {
Callout: wrapper({
label: 'Callout',
icon: megaphoneIcon,
schema: {
type: fields.select({
label: 'Type',
defaultValue: 'default',
options: [
{
label: 'Default',
value: 'default',
},
{
label: 'Example',
value: 'example',
},
{
label: 'Info',
value: 'info',
},
{
label: 'Warning',
value: 'warning',
},
{
label: 'Tip',
value: 'tip',
},
{
label: 'Spoiler',
value: 'spoiler',
},
],
}),
title: fields.text({
label: 'Title',
}),
},
}),
};
export default calloutComponent;

View File

@@ -0,0 +1,35 @@
import { imageIcon } from '@keystar/ui/icon/icons/imageIcon';
import { block } from '@keystatic/core/content-components';
import { fields } from '@keystatic/core';
const figureComponent = {
Figure: block({
label: 'Figure',
icon: imageIcon,
schema: {
src: fields.image({
label: 'Image',
directory: 'public/images/figures',
publicPath: '/images/figures',
}),
alt: fields.text({
label: 'Alt Text',
validation: {
length: {
min: 1,
},
},
}),
caption: fields.text({
label: 'Caption',
multiline: true,
}),
credit: fields.text({
label: 'Credit/Attribution',
description: 'Photographer, artist, or source',
}),
},
}),
};
export default figureComponent;

View File

@@ -2,12 +2,16 @@ import gridComponents from '@/keystatic/components/general/grid';
import sidenoteComponents from '@/keystatic/components/general/sidenote'; import sidenoteComponents from '@/keystatic/components/general/sidenote';
import definitionlistComponents from '@/keystatic/components/general/definitionlist'; import definitionlistComponents from '@/keystatic/components/general/definitionlist';
import accordionComponents from '@/keystatic/components/general/accordion'; import accordionComponents from '@/keystatic/components/general/accordion';
import blockquoteComponents from '@/keystatic/components/general/blockquote'; import blockquoteComponent from '@/keystatic/components/general/blockquote';
import figureComponent from '@/keystatic/components/general/figure';
import calloutComponent from '@/keystatic/components/general/callout';
export const generalComponents = { export const generalComponents = {
...gridComponents, ...gridComponents,
...sidenoteComponents, ...sidenoteComponents,
...definitionlistComponents, ...definitionlistComponents,
...accordionComponents, ...accordionComponents,
...blockquoteComponents, ...blockquoteComponent,
...figureComponent,
...calloutComponent,
}; };

View File

@@ -93,4 +93,35 @@ export const tags: Config['tags'] = {
}, },
}, },
}, },
Figure: {
render: 'Figure',
attributes: {
src: {
type: 'String',
required: true,
},
alt: {
type: 'String',
required: true,
},
caption: {
type: 'String',
},
credit: {
type: 'String',
},
},
},
Callout: {
render: 'Callout',
attributes: {
type: {
type: 'String',
default: 'default',
},
title: {
type: 'String',
},
},
},
}; };

View File

@@ -143,7 +143,7 @@
} }
} }
& p { & p:not([class]) {
margin-block: var(--el-p-vspace-top) var(--el-p-vspace-bottom); margin-block: var(--el-p-vspace-top) var(--el-p-vspace-bottom);
font-family: var(--el-p-font-family), sans-serif; font-family: var(--el-p-font-family), sans-serif;
@@ -495,18 +495,6 @@
text-underline-offset: var(--size-1); text-underline-offset: var(--size-1);
} }
& dfn {
font-weight: var(--typo-weight-bold);
font-style: normal;
color: var(--color-text-primary);
}
& cite {
font-weight: var(--typo-weight-semibold);
font-style: normal;
color: var(--color-text-secondary);
}
& q { & q {
font-style: normal; font-style: normal;