feat: remplace le sprite SVG OKI par les icônes Lucide
- Ajoute @lucide/svelte et le composant Icon avec son mapping - Adapte chaque icône au sens du texte (flag, unlock, handshake, shield, landmark, briefcase, music, clapperboard, mic, file-text, etc.) - Remplace tous les <use href='/icons.svg#...'> par <Icon name='...' /> - Met à jour BrandIcon pour utiliser Lucide Mail - Supprime static/icons.svg devenu inutile - Ajuste les styles scoped avec :global() pour cibler les SVG Lucide - Met à jour README.md et CONTRIBUTING.md
This commit is contained in:
+1
-1
@@ -94,7 +94,7 @@ Les liens de contact sont regroupés dans `src/lib/i18n/fr/contact.json` (et `en
|
||||
- Sections de l'accueil : `src/lib/components/sections/`
|
||||
- Layout : `Nav.svelte`, `Footer.svelte`, `Seo.svelte`
|
||||
- Motion : `src/lib/components/motion/` (KineticText, ScrollProgressBar)
|
||||
- Icônes : sprite `static/icons.svg` (set OKI, charte §4) et `src/lib/components/icons/BrandIcon.svelte` (logos de marques)
|
||||
- Icônes : `@lucide/svelte` via `src/lib/components/icons/Icon.svelte` (mapping sémantique dans `lucide-map.ts`) et `src/lib/components/icons/BrandIcon.svelte` (logos de marques non disponibles dans Lucide)
|
||||
|
||||
Après toute modification d'un composant, passez l'autofixer (voir `svelte-5_code_writer.md`) :
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ src/
|
||||
offline/ # page hors-ligne (cible navigateFallback du SW)
|
||||
+error.svelte # 404/erreurs designée (microcopy KA)
|
||||
static/
|
||||
fonts/ icons.svg images/ assets/press/ theme.js lang-redirect.js
|
||||
fonts/ images/ assets/press/ theme.js lang-redirect.js
|
||||
manifest.webmanifest 404.html robots.txt sitemap.xml _headers .htaccess
|
||||
```
|
||||
|
||||
|
||||
Generated
+10
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@fontsource/archivo": "^5.3.0",
|
||||
"@fontsource/inter": "^5.3.0",
|
||||
"@lucide/svelte": "^1.30.0",
|
||||
"gsap": "^3.15.0",
|
||||
"lenis": "^1.3.25"
|
||||
},
|
||||
@@ -2285,6 +2286,15 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@lucide/svelte": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.30.0.tgz",
|
||||
"integrity": "sha512-KtVrqWT3BD41kf1CPaP4QTAyofw0xm5hw/JGwd76+wMk1l0ZcXskA2Wi9ANVWCsk7mGrpFo2SqL3Hl29xTkV3w==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"svelte": "^5"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"dependencies": {
|
||||
"@fontsource/archivo": "^5.3.0",
|
||||
"@fontsource/inter": "^5.3.0",
|
||||
"@lucide/svelte": "^1.30.0",
|
||||
"gsap": "^3.15.0",
|
||||
"lenis": "^1.3.25"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { page } from '$app/state';
|
||||
import { resolve } from '$app/paths';
|
||||
import { onMount } from 'svelte';
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import { alternatePath, type Bundle, type Locale } from '$lib/i18n';
|
||||
|
||||
let { t, locale }: { t: Bundle; locale: Locale } = $props();
|
||||
@@ -124,13 +125,8 @@
|
||||
title={t.ui.theme_toggle}
|
||||
onclick={toggleTheme}
|
||||
>
|
||||
<svg class="theme-icon theme-icon-dark" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M20 14.5A8.5 8.5 0 0 1 9.5 4 8.5 8.5 0 1 0 20 14.5Z" />
|
||||
</svg>
|
||||
<svg class="theme-icon theme-icon-light" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="4.5" />
|
||||
<path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.5 4.5l2 2M17.5 17.5l2 2M19.5 4.5l-2 2M6.5 17.5l-2 2" />
|
||||
</svg>
|
||||
<Icon name="moon" class="theme-icon theme-icon-dark" />
|
||||
<Icon name="sun" class="theme-icon theme-icon-light" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -270,24 +266,21 @@
|
||||
border-color: var(--or-oki);
|
||||
}
|
||||
|
||||
.theme-icon {
|
||||
.theme-toggle :global(.theme-icon) {
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: square;
|
||||
}
|
||||
|
||||
.theme-icon-light {
|
||||
.theme-toggle :global(.theme-icon-light) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:global(html.light-theme) .theme-icon-dark {
|
||||
:global(html.light-theme .theme-toggle .theme-icon-dark) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:global(html.light-theme) .theme-icon-light {
|
||||
:global(html.light-theme .theme-toggle .theme-icon-light) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Mail from '@lucide/svelte/icons/mail';
|
||||
|
||||
interface BrandPath {
|
||||
d: string;
|
||||
fillRule?: 'evenodd';
|
||||
@@ -40,14 +42,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
email: {
|
||||
viewBox: '0 0 24 24',
|
||||
paths: [
|
||||
{
|
||||
d: 'M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z'
|
||||
}
|
||||
]
|
||||
},
|
||||
gitea: {
|
||||
viewBox: '5.67 143.05 628.65 387.55',
|
||||
paths: [
|
||||
@@ -165,7 +159,9 @@
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#if def}
|
||||
{#if name === 'email'}
|
||||
<Mail class={className} size="1em" />
|
||||
{:else if def}
|
||||
<svg
|
||||
viewBox={def.viewBox}
|
||||
fill="currentColor"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { LUCIDE_ICONS } from './lucide-map';
|
||||
|
||||
interface Props {
|
||||
/** Nom d'une icône Lucide enregistrée dans lucide-map. */
|
||||
name: string;
|
||||
class?: string;
|
||||
size?: number | string;
|
||||
strokeWidth?: number;
|
||||
}
|
||||
|
||||
let { name, class: className = '', size = '1em', strokeWidth = 2 }: Props = $props();
|
||||
|
||||
const Icon = $derived(LUCIDE_ICONS[name]);
|
||||
</script>
|
||||
|
||||
{#if Icon}
|
||||
<Icon class={className} {size} {strokeWidth} />
|
||||
{/if}
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { Component } from 'svelte';
|
||||
|
||||
import BadgePercent from '@lucide/svelte/icons/badge-percent';
|
||||
import BookOpen from '@lucide/svelte/icons/book-open';
|
||||
import Briefcase from '@lucide/svelte/icons/briefcase';
|
||||
import CircleHelp from '@lucide/svelte/icons/circle-help';
|
||||
import CircleX from '@lucide/svelte/icons/circle-x';
|
||||
import Clapperboard from '@lucide/svelte/icons/clapperboard';
|
||||
import Code from '@lucide/svelte/icons/code';
|
||||
import Coffee from '@lucide/svelte/icons/coffee';
|
||||
import FileText from '@lucide/svelte/icons/file-text';
|
||||
import Flag from '@lucide/svelte/icons/flag';
|
||||
import Globe from '@lucide/svelte/icons/globe';
|
||||
import Handshake from '@lucide/svelte/icons/handshake';
|
||||
import Heart from '@lucide/svelte/icons/heart';
|
||||
import Landmark from '@lucide/svelte/icons/landmark';
|
||||
import Megaphone from '@lucide/svelte/icons/megaphone';
|
||||
import MessageCircle from '@lucide/svelte/icons/message-circle';
|
||||
import Mic from '@lucide/svelte/icons/mic';
|
||||
import Moon from '@lucide/svelte/icons/moon';
|
||||
import Music from '@lucide/svelte/icons/music';
|
||||
import Music2 from '@lucide/svelte/icons/music-2';
|
||||
import Newspaper from '@lucide/svelte/icons/newspaper';
|
||||
import Plus from '@lucide/svelte/icons/plus';
|
||||
import Shield from '@lucide/svelte/icons/shield';
|
||||
import Sprout from '@lucide/svelte/icons/sprout';
|
||||
import Star from '@lucide/svelte/icons/star';
|
||||
import Sun from '@lucide/svelte/icons/sun';
|
||||
import Unlock from '@lucide/svelte/icons/unlock';
|
||||
import WifiOff from '@lucide/svelte/icons/wifi-off';
|
||||
|
||||
export const LUCIDE_ICONS: Record<string, Component> = {
|
||||
'badge-percent': BadgePercent,
|
||||
'book-open': BookOpen,
|
||||
briefcase: Briefcase,
|
||||
'circle-help': CircleHelp,
|
||||
'circle-x': CircleX,
|
||||
clapperboard: Clapperboard,
|
||||
code: Code,
|
||||
coffee: Coffee,
|
||||
'file-text': FileText,
|
||||
flag: Flag,
|
||||
globe: Globe,
|
||||
handshake: Handshake,
|
||||
heart: Heart,
|
||||
landmark: Landmark,
|
||||
megaphone: Megaphone,
|
||||
'message-circle': MessageCircle,
|
||||
mic: Mic,
|
||||
moon: Moon,
|
||||
music: Music,
|
||||
'music-2': Music2,
|
||||
newspaper: Newspaper,
|
||||
plus: Plus,
|
||||
shield: Shield,
|
||||
sprout: Sprout,
|
||||
star: Star,
|
||||
sun: Sun,
|
||||
unlock: Unlock,
|
||||
'wifi-off': WifiOff
|
||||
};
|
||||
|
||||
export type LucideIconName = keyof typeof LUCIDE_ICONS;
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import type { Bundle } from '$lib/i18n';
|
||||
|
||||
/**
|
||||
@@ -22,7 +23,7 @@
|
||||
<div class="marquee-group">
|
||||
{#each items as item, i (i)}
|
||||
<span class="marquee-item">{item}</span>
|
||||
<svg class="icon marquee-star" aria-hidden="true"><use href="/icons.svg#zetwal" /></svg>
|
||||
<Icon name="star" class="icon marquee-star" />
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
@@ -75,7 +76,7 @@
|
||||
color: var(--or-oki);
|
||||
}
|
||||
|
||||
.marquee-star {
|
||||
:global(.marquee-star) {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
color: var(--or-oki);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import BrandIcon from '$lib/components/icons/BrandIcon.svelte';
|
||||
import FlagChip from '$lib/components/motion/FlagChip.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
@@ -50,7 +51,7 @@
|
||||
<div class="contact-socials-group" use:reveal={i}>
|
||||
<h3 class="contact-socials-title">
|
||||
{#if title.icon}
|
||||
<svg class="icon" aria-hidden="true"><use href="/icons.svg#{title.icon}" /></svg>
|
||||
<Icon name={title.icon} class="icon" />
|
||||
{/if}
|
||||
{title.text}
|
||||
</h3>
|
||||
@@ -123,7 +124,7 @@
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.contact-socials-title .icon {
|
||||
.contact-socials-title :global(.icon) {
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import FlagChip from '$lib/components/motion/FlagChip.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
import ResponsiveImage from '$lib/components/ResponsiveImage.svelte';
|
||||
@@ -73,9 +74,7 @@
|
||||
{#each t.djangokam.faits as fait, i (fait.chiffre)}
|
||||
<div class="djangokam-fact-card" use:reveal={i}>
|
||||
<div class="djangokam-fact-icon reveal-pop" use:reveal={i + 1}>
|
||||
<svg class="icon" aria-hidden="true"
|
||||
><use href="/icons.svg#{pictoFor(fait.icon)}" /></svg
|
||||
>
|
||||
<Icon name={pictoFor(fait.icon)} class="icon" />
|
||||
</div>
|
||||
<h3>{fait.chiffre}</h3>
|
||||
<!-- Contenu éditorial du bundle i18n (liens internes balisés). -->
|
||||
@@ -297,7 +296,7 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.djangokam-fact-icon .icon {
|
||||
.djangokam-fact-icon :global(.icon) {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import FlagChip from '$lib/components/motion/FlagChip.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
import type { Bundle, Locale } from '$lib/i18n';
|
||||
@@ -23,9 +24,7 @@
|
||||
{#each t.engagement.cards as card, i (card.title)}
|
||||
<div class="engagement-card" use:reveal={i}>
|
||||
<div class="engagement-icon reveal-pop" use:reveal={i + 1}>
|
||||
<svg class="icon" aria-hidden="true"
|
||||
><use href="/icons.svg#{pictoFor(card.icon)}" /></svg
|
||||
>
|
||||
<Icon name={pictoFor(card.icon)} class="icon" />
|
||||
</div>
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.text}</p>
|
||||
@@ -77,7 +76,7 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.engagement-icon .icon {
|
||||
.engagement-icon :global(.icon) {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import FlagChip from '$lib/components/motion/FlagChip.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
import type { Bundle, Locale } from '$lib/i18n';
|
||||
@@ -23,7 +24,7 @@
|
||||
<details class="faq-item" use:reveal={i}>
|
||||
<summary class="faq-question">
|
||||
<span>{item.question}</span>
|
||||
<span class="faq-icon" aria-hidden="true">+</span>
|
||||
<Icon name="plus" class="faq-icon" size={22} />
|
||||
</summary>
|
||||
<div class="faq-answer">
|
||||
<p>{item.answer}</p>
|
||||
@@ -98,14 +99,13 @@
|
||||
color: var(--or-oki);
|
||||
}
|
||||
|
||||
.faq-icon {
|
||||
font-size: 1.4rem;
|
||||
:global(.faq-icon) {
|
||||
color: var(--or-oki);
|
||||
flex-shrink: 0;
|
||||
transition: transform var(--dur-tanbou) var(--ease-ka);
|
||||
}
|
||||
|
||||
.faq-item[open] .faq-icon {
|
||||
.faq-item[open] :global(.faq-icon) {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import type { Bundle, Locale } from '$lib/i18n';
|
||||
import { splitLeadingEmoji } from './pictos';
|
||||
|
||||
@@ -25,7 +26,7 @@
|
||||
{@const tag = splitLeadingEmoji(rawTag)}
|
||||
<span class="tag">
|
||||
{#if tag.icon}
|
||||
<svg class="icon" aria-hidden="true"><use href="/icons.svg#{tag.icon}" /></svg>
|
||||
<Icon name={tag.icon} class="icon" />
|
||||
{/if}
|
||||
{tag.text}
|
||||
</span>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import FlagChip from '$lib/components/motion/FlagChip.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
import type { Bundle, Locale } from '$lib/i18n';
|
||||
@@ -24,9 +25,7 @@
|
||||
{#each t.values as value, i (value.title)}
|
||||
<div class="value-card" use:reveal={i}>
|
||||
<div class="value-icon reveal-pop" use:reveal={i + 1}>
|
||||
<svg class="icon" aria-hidden="true"
|
||||
><use href="/icons.svg#{pictoFor(value.icon)}" /></svg
|
||||
>
|
||||
<Icon name={pictoFor(value.icon)} class="icon" />
|
||||
</div>
|
||||
<h3>{value.title}</h3>
|
||||
<p>{value.description}</p>
|
||||
@@ -88,7 +87,7 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.value-icon .icon {
|
||||
.value-icon :global(.icon) {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import FlagChip from '$lib/components/motion/FlagChip.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
import type { Bundle, Locale } from '$lib/i18n';
|
||||
@@ -28,9 +29,7 @@
|
||||
use:reveal={i}
|
||||
>
|
||||
<div class="project-icon reveal-pop" use:reveal={i + 1}>
|
||||
<svg class="icon" aria-hidden="true"
|
||||
><use href="/icons.svg#{pictoFor(item.icon)}" /></svg
|
||||
>
|
||||
<Icon name={pictoFor(item.icon)} class="icon" />
|
||||
</div>
|
||||
<h3>{item.name}</h3>
|
||||
<p class="project-domain">{item.domain}</p>
|
||||
@@ -82,7 +81,7 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.project-icon .icon {
|
||||
.project-icon :global(.icon) {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import FlagChip from '$lib/components/motion/FlagChip.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
import type { Bundle, Locale } from '$lib/i18n';
|
||||
@@ -23,9 +24,7 @@
|
||||
{#each t.publics.items as item, i (item.title)}
|
||||
<div class="public-card" use:reveal={i}>
|
||||
<div class="public-icon reveal-pop" use:reveal={i + 1}>
|
||||
<svg class="icon" aria-hidden="true"
|
||||
><use href="/icons.svg#{pictoFor(item.icon)}" /></svg
|
||||
>
|
||||
<Icon name={pictoFor(item.icon)} class="icon" />
|
||||
</div>
|
||||
<h3>{item.title}</h3>
|
||||
<p class="public-description">{item.description}</p>
|
||||
@@ -97,7 +96,7 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.public-icon .icon {
|
||||
.public-icon :global(.icon) {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
@@ -1,42 +1,45 @@
|
||||
/**
|
||||
* Mapping emojis legacy → pictos du sprite OKI (`/icons.svg`).
|
||||
* Mapping emojis legacy → noms d'icônes Lucide.
|
||||
* Charte §4 : zéro emoji décoratif en production.
|
||||
* Les clés sont normalisées (sans sélecteur de variation U+FE0F
|
||||
* ni modificateur de teint U+1F3FB–U+1F3FF) pour fiabiliser la
|
||||
* correspondance avec les chaînes des JSON i18n.
|
||||
*
|
||||
* Le choix des icônes est guidé par le sens du texte associé,
|
||||
* pas par la traduction littérale de l'emoji.
|
||||
*/
|
||||
|
||||
const PICTOS: Record<string, string> = {
|
||||
'🌐': 'lanme', // web, liens internationaux
|
||||
'📢': 'lambi', // annonces, chaînes
|
||||
'💬': 'pawol', // communauté, discussion
|
||||
'💻': 'kle', // code, open-source
|
||||
'✊': 'glo', // luttes, tarifs solidaires
|
||||
'💸': 'glo', // tarifs préférentiels / solidaires
|
||||
'🔓': 'kle', // liberté, open-source
|
||||
'🤝': 'jaden', // solidarité
|
||||
'🛡': 'mawon', // souveraineté
|
||||
'💼': 'zetwal', // entreprises responsables
|
||||
'🏛': 'kannen', // collectifs, patrimoine
|
||||
'🎵': 'mizik-note', // sorties musicales
|
||||
'🎶': 'mizik-note', // transcriptions musicales (PAWOL.NU)
|
||||
'🎬': 'zetwal', // clips / plateforme multimédia
|
||||
'🎤': 'glo', // artistes engagés
|
||||
'📝': 'pawol', // paroles & traductions
|
||||
'📚': 'kannen' // documents historiques (GONG.GP)
|
||||
'🌐': 'globe', // web, liens internationaux
|
||||
'📢': 'megaphone', // annonces, chaînes
|
||||
'💬': 'message-circle', // communauté, discussion
|
||||
'💻': 'code', // code, open-source
|
||||
'✊': 'flag', // luttes, engagement militant
|
||||
'💸': 'badge-percent', // tarifs préférentiels / solidaires
|
||||
'🔓': 'unlock', // liberté, open-source
|
||||
'🤝': 'handshake', // solidarité
|
||||
'🛡': 'shield', // souveraineté
|
||||
'💼': 'briefcase', // entreprises responsables
|
||||
'🏛': 'landmark', // collectifs, patrimoine
|
||||
'🎵': 'music', // sorties musicales
|
||||
'🎶': 'music-2', // transcriptions musicales (PAWOL.NU)
|
||||
'🎬': 'clapperboard', // clips / plateforme multimédia
|
||||
'🎤': 'mic', // artistes engagés
|
||||
'📝': 'file-text', // paroles & traductions
|
||||
'📚': 'book-open' // documents historiques (GONG.GP)
|
||||
};
|
||||
|
||||
/** Picto du sprite correspondant à un emoji legacy (`ka` en repli). */
|
||||
/** Nom d'icône Lucide correspondant à un emoji legacy (`circle-help` en repli). */
|
||||
export function pictoFor(icon: string): string {
|
||||
const key = icon.replace(/\uFE0E|\uFE0F/g, '').replace(/[\u{1F3FB}-\u{1F3FF}]/gu, '');
|
||||
return PICTOS[key] ?? 'ka';
|
||||
return PICTOS[key] ?? 'circle-help';
|
||||
}
|
||||
|
||||
const LEADING_EMOJI = /^(?<emoji>\p{Extended_Pictographic}\uFE0F?[\u{1F3FB}-\u{1F3FF}]?)\s*/u;
|
||||
|
||||
/**
|
||||
* Sépare un emoji en tête d'une chaîne de contenu (tags hero, etc.) :
|
||||
* l'emoji devient un picto du sprite, le texte reste intact.
|
||||
* l'emoji devient un nom d'icône Lucide, le texte reste intact.
|
||||
*/
|
||||
export function splitLeadingEmoji(raw: string): { icon: string | null; text: string } {
|
||||
const match = raw.match(LEADING_EMOJI);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { resolve } from '$app/paths';
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
|
||||
const status = $derived(page.status);
|
||||
const is404 = $derived(status === 404);
|
||||
@@ -12,7 +13,7 @@
|
||||
</svelte:head>
|
||||
|
||||
<section class="error-page container">
|
||||
<svg class="icon error-icon" aria-hidden="true"><use href="/icons.svg#ka" /></svg>
|
||||
<Icon name="circle-x" class="icon error-icon" size={96} />
|
||||
<h1>{status}</h1>
|
||||
{#if is404}
|
||||
<p class="ka" lang="gcf">« Paj la rivé périyòd… »</p>
|
||||
@@ -33,7 +34,7 @@
|
||||
padding: var(--space-5) var(--space-4);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
:global(.error-icon) {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
color: var(--or-oki);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import Seo from '$lib/components/Seo.svelte';
|
||||
import donations from '$lib/data/donations.json';
|
||||
import { getBundle } from '$lib/i18n';
|
||||
@@ -41,7 +42,7 @@
|
||||
<div class="donation-panels">
|
||||
<div class="support-panel support-panel--primary">
|
||||
<h2>
|
||||
<svg class="icon" aria-hidden="true"><use href="/icons.svg#lakanmou" /></svg>
|
||||
<Icon name="heart" class="icon" />
|
||||
{t.support.one_time_title}
|
||||
</h2>
|
||||
|
||||
@@ -130,9 +131,7 @@
|
||||
rel="external noopener noreferrer"
|
||||
class="alt-donation-card alt-donation-card--liberapay"
|
||||
>
|
||||
<svg class="icon alt-donation-icon" aria-hidden="true"
|
||||
><use href="/icons.svg#jaden" /></svg
|
||||
>
|
||||
<Icon name="sprout" class="icon alt-donation-icon" />
|
||||
<h3>{t.support.liberapay_title}</h3>
|
||||
<p>{t.support.liberapay_text}</p>
|
||||
</a>
|
||||
@@ -143,9 +142,7 @@
|
||||
rel="external noopener noreferrer"
|
||||
class="alt-donation-card alt-donation-card--kofi"
|
||||
>
|
||||
<svg class="icon alt-donation-icon" aria-hidden="true"
|
||||
><use href="/icons.svg#lakanmou" /></svg
|
||||
>
|
||||
<Icon name="coffee" class="icon alt-donation-icon" />
|
||||
<h3>{t.support.kofi_title}</h3>
|
||||
<p>{t.support.kofi_text}</p>
|
||||
</a>
|
||||
@@ -394,7 +391,7 @@
|
||||
--card-accent: var(--or-oki);
|
||||
}
|
||||
|
||||
.alt-donation-icon {
|
||||
:global(.alt-donation-icon) {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: var(--card-accent, var(--or-oki));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import Seo from '$lib/components/Seo.svelte';
|
||||
import donations from '$lib/data/donations.json';
|
||||
import { getBundle } from '$lib/i18n';
|
||||
@@ -41,7 +42,7 @@
|
||||
<div class="donation-panels">
|
||||
<div class="support-panel support-panel--primary">
|
||||
<h2>
|
||||
<svg class="icon" aria-hidden="true"><use href="/icons.svg#lakanmou" /></svg>
|
||||
<Icon name="heart" class="icon" />
|
||||
{t.support.one_time_title}
|
||||
</h2>
|
||||
|
||||
@@ -130,9 +131,7 @@
|
||||
rel="external noopener noreferrer"
|
||||
class="alt-donation-card alt-donation-card--liberapay"
|
||||
>
|
||||
<svg class="icon alt-donation-icon" aria-hidden="true"
|
||||
><use href="/icons.svg#jaden" /></svg
|
||||
>
|
||||
<Icon name="sprout" class="icon alt-donation-icon" />
|
||||
<h3>{t.support.liberapay_title}</h3>
|
||||
<p>{t.support.liberapay_text}</p>
|
||||
</a>
|
||||
@@ -143,9 +142,7 @@
|
||||
rel="external noopener noreferrer"
|
||||
class="alt-donation-card alt-donation-card--kofi"
|
||||
>
|
||||
<svg class="icon alt-donation-icon" aria-hidden="true"
|
||||
><use href="/icons.svg#lakanmou" /></svg
|
||||
>
|
||||
<Icon name="coffee" class="icon alt-donation-icon" />
|
||||
<h3>{t.support.kofi_title}</h3>
|
||||
<p>{t.support.kofi_text}</p>
|
||||
</a>
|
||||
@@ -394,7 +391,7 @@
|
||||
--card-accent: var(--or-oki);
|
||||
}
|
||||
|
||||
.alt-donation-icon {
|
||||
:global(.alt-donation-icon) {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: var(--card-accent, var(--or-oki));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { asset } from '$app/paths';
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import ResponsiveImage from '$lib/components/ResponsiveImage.svelte';
|
||||
import Seo from '$lib/components/Seo.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
@@ -23,7 +24,7 @@
|
||||
/>
|
||||
|
||||
<section class="press-hero">
|
||||
<svg class="icon hero-icon" aria-hidden="true"><use href="/icons.svg#lambi" /></svg>
|
||||
<Icon name="newspaper" class="icon hero-icon" />
|
||||
<h1>{t.press.title} <span class="accent">{t.press.title_accent}</span></h1>
|
||||
<p class="press-lede">{t.press.lede}</p>
|
||||
</section>
|
||||
@@ -93,7 +94,7 @@
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.hero-icon {
|
||||
:global(.hero-icon) {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
margin: 0 auto var(--space-2);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -8,7 +9,7 @@
|
||||
</svelte:head>
|
||||
|
||||
<section class="offline-page container">
|
||||
<svg class="icon offline-icon" aria-hidden="true"><use href="/icons.svg#lanme" /></svg>
|
||||
<Icon name="wifi-off" class="icon offline-icon" size={96} />
|
||||
<h1>Hors ligne</h1>
|
||||
<p class="ka" lang="gcf">« Konèksyon an koupé. »</p>
|
||||
<p class="fr">
|
||||
@@ -29,7 +30,7 @@
|
||||
padding: var(--space-5) var(--space-4);
|
||||
}
|
||||
|
||||
.offline-icon {
|
||||
:global(.offline-icon) {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
color: var(--or-oki);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { asset } from '$app/paths';
|
||||
import Icon from '$lib/components/icons/Icon.svelte';
|
||||
import ResponsiveImage from '$lib/components/ResponsiveImage.svelte';
|
||||
import Seo from '$lib/components/Seo.svelte';
|
||||
import KineticText from '$lib/components/motion/KineticText.svelte';
|
||||
@@ -23,7 +24,7 @@
|
||||
/>
|
||||
|
||||
<section class="press-hero">
|
||||
<svg class="icon hero-icon" aria-hidden="true"><use href="/icons.svg#lambi" /></svg>
|
||||
<Icon name="newspaper" class="icon hero-icon" />
|
||||
<h1>{t.press.title} <span class="accent">{t.press.title_accent}</span></h1>
|
||||
<p class="press-lede">{t.press.lede}</p>
|
||||
</section>
|
||||
@@ -93,7 +94,7 @@
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.hero-icon {
|
||||
:global(.hero-icon) {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
margin: 0 auto var(--space-2);
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Set iconographique OKI — charte §4 : viewBox 24×24, stroke 2 px, angles nets, currentColor -->
|
||||
<symbol id="ka" viewBox="0 0 24 24">
|
||||
<!-- tambour ka : collerette hexagonale, fût conique, sangles en zigzag -->
|
||||
<path d="M4 7 L20 7 L22 9 L20 11 L4 11 L2 9 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M4 11 L7 21 L17 21 L20 11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M8 11 L10.5 16 L9 21 M16 11 L13.5 16 L15 21" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="lambi" viewBox="0 0 24 24">
|
||||
<!-- conque lambi : dome en spirale, lèvre angulaire -->
|
||||
<path d="M4 14 C4 8 9 4 14 4 C18 4 21 7 21 11 L17 14 L20 18 L13 20 C8 21 4 18 4 14 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M11 14 C11 12.5 13.5 12.5 13.5 14 C13.5 16 9.5 16 9.5 13 C9.5 9.5 14 8.5 16 10.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="zetwal" viewBox="0 0 24 24">
|
||||
<!-- étoile 4 branches -->
|
||||
<path d="M12 3 L14.5 9.5 L21 12 L14.5 14.5 L12 21 L9.5 14.5 L3 12 L9.5 9.5 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="mawon" viewBox="0 0 24 24">
|
||||
<!-- brisure de chaîne : deux demi-maillons octogonaux, connexion rompue -->
|
||||
<path d="M9 7 L5 7 L3 9 L3 15 L5 17 L9 17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M15 7 L19 7 L21 9 L21 15 L19 17 L15 17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M12 9.5 L12 11 M12 13 L12 14.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="lakanmou" viewBox="0 0 24 24">
|
||||
<!-- flamme : contour angulaire à base courbe, languette interne -->
|
||||
<path d="M12 3 L15 8 L18 12 C19 15 18 18 15.5 20 L12 21 L8.5 20 C6 18 5 15 6 12 L9 8 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M12 12 L14 14.5 C14.3 16.5 13 18 12 18 C11 18 9.7 16.5 10 14.5 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="jaden" viewBox="0 0 24 24">
|
||||
<!-- pousse : tige et deux feuilles angulaires en cerf-volant -->
|
||||
<path d="M12 21 L12 10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M12 13 L3 5 L6 12 L10 16 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M12 13 L21 5 L18 12 L14 16 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="kannen" viewBox="0 0 24 24">
|
||||
<!-- canne à sucre : tige segmentée, trois lames au sommet -->
|
||||
<path d="M10 21 L10 6 L14 6 L14 21 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M10 17 L14 17 M10 13 L14 13 M10 9 L14 9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M10 6 L5 3 M14 6 L19 3 M12 6 L12 2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="lanme" viewBox="0 0 24 24">
|
||||
<!-- vague angulaire : trois lignes brisées superposées -->
|
||||
<path d="M2 8 L7 4 L12 8 L17 4 L22 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M2 14 L7 10 L12 14 L17 10 L22 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M2 20 L7 16 L12 20 L17 16 L22 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="glo" viewBox="0 0 24 24">
|
||||
<!-- poing levé géométrique : bloc poing + avant-bras, doigts segmentés, pouce -->
|
||||
<path d="M8 20 L8 15 L6 15 L6 6 L8 4 L18 4 L18 15 L16 15 L16 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M6 9 L18 9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M9 4 L9 9 M12 4 L12 9 M15 4 L15 9 M6 12 L10 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="kle" viewBox="0 0 24 24">
|
||||
<!-- cadenas ouvert : corps rectangulaire, anse angulaire levée -->
|
||||
<path d="M5 11 L19 11 L19 21 L5 21 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M8 11 L8 6 L10 4 L14 4 L16 6 L16 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M12 14.5 L12 17.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="pawol" viewBox="0 0 24 24">
|
||||
<!-- bulle de parole angulaire : rectangle à queue, deux lignes de texte -->
|
||||
<path d="M3 4 L21 4 L21 15 L13 15 L8 20 L8 15 L3 15 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M7 8 L17 8 M7 11.5 L13 11.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
<symbol id="mizik-note" viewBox="0 0 24 24">
|
||||
<!-- double croche : deux tiges reliées par un croc, têtes en losange -->
|
||||
<path d="M9 6 L9 19 M19 4 L19 17 M9 6 L19 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M9 16.5 L11.5 19 L9 21.5 L6.5 19 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
<path d="M19 14.5 L21.5 17 L19 19.5 L16.5 17 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.2 KiB |
Reference in New Issue
Block a user