<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wolf</title>
    <description>The latest articles on DEV Community by Wolf (@serotoninja).</description>
    <link>https://dev.to/serotoninja</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1591551%2F561a4062-87b5-4d9e-9a46-6875a3e526de.jpg</url>
      <title>DEV Community: Wolf</title>
      <link>https://dev.to/serotoninja</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/serotoninja"/>
    <language>en</language>
    <item>
      <title>UI Kernel: Design Tokens as Symfony infrastructure</title>
      <dc:creator>Wolf</dc:creator>
      <pubDate>Fri, 03 Jul 2026 22:34:20 +0000</pubDate>
      <link>https://dev.to/symfinity/ui-kernel-design-tokens-as-symfony-infrastructure-31m</link>
      <guid>https://dev.to/symfinity/ui-kernel-design-tokens-as-symfony-infrastructure-31m</guid>
      <description>&lt;p&gt;For years, Symfony teams solved UI scaling one of two ways.&lt;/p&gt;

&lt;p&gt;Either you adopted a JavaScript build pipeline — Webpack Encore, Vite, npm scripts — and a utility CSS framework like Tailwind.&lt;/p&gt;

&lt;p&gt;Or you stayed "PHP-first" and watched every project grow its own &lt;code&gt;app.css&lt;/code&gt;, its own spacing scale, its own dark-mode hacks, and its own Bootstrap overrides.&lt;/p&gt;

&lt;p&gt;Both paths work. Neither scales cleanly across agencies, products, and long-lived admin UIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI Kernel&lt;/strong&gt; (&lt;code&gt;symfinity/ui-kernel&lt;/code&gt;, current line &lt;strong&gt;0.3.x&lt;/strong&gt;) is Symfinity's answer on the Symfony side: treat &lt;strong&gt;design tokens as infrastructure&lt;/strong&gt; — resolved in PHP, emitted as CSS custom properties, consumed by Twig and component CSS — without making Node the gatekeeper of your theme.&lt;/p&gt;

&lt;p&gt;This article explains the CSS story first, then where Symfony fits in — including the &lt;strong&gt;0.3.x theme engine&lt;/strong&gt; (runtime switching, overlays, and inheritance).&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem tokens solve
&lt;/h2&gt;

&lt;p&gt;Hard-coded hex values in templates do not scale.&lt;/p&gt;

&lt;p&gt;When primary blue lives in forty places, rebranding is a grep exercise. When dark mode is bolted on with &lt;code&gt;@media (prefers-color-scheme: dark)&lt;/code&gt; and ad hoc overrides, contrast breaks in corners nobody tested. When spacing is "whatever looked fine in Figma," components drift apart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design tokens&lt;/strong&gt; are the fix: named, semantic variables (&lt;code&gt;--color-text&lt;/code&gt;, &lt;code&gt;--space-md&lt;/code&gt;, &lt;code&gt;--radius-lg&lt;/code&gt;) that components reference instead of raw values.&lt;/p&gt;

&lt;p&gt;That idea is not new. What changed recently is &lt;em&gt;how&lt;/em&gt; we author and validate those tokens in CSS — especially for colour.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why OKLCH matters for palettes
&lt;/h2&gt;

&lt;p&gt;Classic spaces — &lt;strong&gt;RGB&lt;/strong&gt;, &lt;strong&gt;HSL&lt;/strong&gt; — are easy to compute but bad at matching human perception.&lt;/p&gt;

&lt;p&gt;A "50% lightness" yellow and a "50% lightness" blue do not look equally bright. Darkening with &lt;code&gt;hsl()&lt;/code&gt; or Sass &lt;code&gt;darken()&lt;/code&gt; shifts hue and brightness in unpredictable ways. Building a ten-step brand ramp that stays accessible was always guesswork.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OKLCH&lt;/strong&gt; (lightness, chroma, hue in the Oklab model) is &lt;strong&gt;perceptually uniform&lt;/strong&gt;: equal steps on the L axis feel like equal brightness changes across hues. That makes ramps, states (&lt;code&gt;hover&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt;), and dark-mode pairs far more predictable.&lt;/p&gt;

&lt;p&gt;In modern CSS you can write tokens directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.30&lt;/span&gt; &lt;span class="m"&gt;0.03&lt;/span&gt; &lt;span class="m"&gt;260&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.97&lt;/span&gt; &lt;span class="m"&gt;0.01&lt;/span&gt; &lt;span class="m"&gt;260&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.60&lt;/span&gt; &lt;span class="m"&gt;0.14&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Progressive enhancement still matters — ship sRGB fallbacks, layer OKLCH behind &lt;code&gt;@supports&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#222326&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#005fcc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.5&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.30&lt;/span&gt; &lt;span class="m"&gt;0.03&lt;/span&gt; &lt;span class="m"&gt;260&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.60&lt;/span&gt; &lt;span class="m"&gt;0.14&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For state colours, &lt;strong&gt;&lt;code&gt;color-mix(in oklch, …)&lt;/code&gt;&lt;/strong&gt; keeps blends on the perceptual axis instead of muddy RGB averages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contrast: WCAG today, APCA tomorrow
&lt;/h3&gt;

&lt;p&gt;Accessibility checks still anchor on &lt;strong&gt;WCAG 2.x contrast ratios&lt;/strong&gt; (4.5:1 body text, 3:1 large text/UI). OKLCH helps because &lt;strong&gt;L tracks perceived lightness&lt;/strong&gt; — pairs with sufficient L separation tend to pass, which is why token-driven systems can enforce contrast at generation time instead of in every component file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;APCA&lt;/strong&gt; (Advanced Perceptual Contrast Algorithm) is the direction of travel for WCAG 3. Good practice in 2026: design with APCA awareness, audit with WCAG for compliance paperwork. They usually agree; where they diverge, APCA often reflects real-world legibility better (thin type, dark UI, wide gamut).&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools worth bookmarking
&lt;/h2&gt;

&lt;p&gt;These are the references I reach for when building or reviewing token palettes — independent of Symfony:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OKLCH Color Picker &amp;amp; Converter&lt;/strong&gt; — &lt;a href="https://oklch.com/" rel="noopener noreferrer"&gt;oklch.com&lt;/a&gt; — pick, convert, P3/Rec2020 preview, palette building with APCA (Evil Martians)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evil Martians OKLCH ecosystem guide&lt;/strong&gt; — &lt;a href="https://evilmartians.com/chronicles/exploring-the-oklch-ecosystem-and-its-tools" rel="noopener noreferrer"&gt;Exploring the OKLCH ecosystem&lt;/a&gt; — Harmonizer, apcach, and related tooling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atmos OKLCH picker&lt;/strong&gt; — &lt;a href="https://atmos.style/color-picker/oklch" rel="noopener noreferrer"&gt;atmos.style/color-picker/oklch&lt;/a&gt; — fast format conversion plus WCAG/APCA hints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HubTools color picker&lt;/strong&gt; — &lt;a href="https://www.hub-tools.com/tools/color-picker" rel="noopener noreferrer"&gt;hub-tools.com/tools/color-picker&lt;/a&gt; — OKLCH/LAB/P3, harmonies, CVD simulation, W3C token export&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APCA introduction&lt;/strong&gt; — &lt;a href="https://git.apcacontrast.com/documentation/APCAeasyIntro.html" rel="noopener noreferrer"&gt;APCA easy intro&lt;/a&gt; — readable explainer for the contrast model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;W3C Design Tokens Community Group&lt;/strong&gt; — &lt;a href="https://www.designtokens.org/" rel="noopener noreferrer"&gt;designtokens.org&lt;/a&gt; — format spec for portable token files&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Further reading (CSS and design systems)
&lt;/h2&gt;

&lt;p&gt;These DEV Community articles cover the &lt;em&gt;CSS&lt;/em&gt; side better than any Symfony doc should duplicate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/_d7eb1c1703182e3ce1782/css-color-picker-hex-rgb-amp-hsl-converter-guide-for-developers-1l69"&gt;CSS Color Picker: HEX, RGB &amp;amp; HSL Converter Guide for Developers&lt;/a&gt; — DEV Community — OKLCH ramps, &lt;code&gt;color-mix(in oklch, …)&lt;/code&gt;, &lt;code&gt;@supports&lt;/code&gt; fallbacks, dark mode via &lt;code&gt;prefers-color-scheme&lt;/code&gt; (pair with &lt;a href="https://dev.to/accessibly_speaking/making-animations-and-transitions-accessible-a-guide-for-developers-3ih9"&gt;Making Animations and Transitions Accessible&lt;/a&gt; for &lt;code&gt;prefers-reduced-motion&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/alphashark/color-spaces-for-developers-why-your-eyes-disagree-with-the-math-4e2b"&gt;Color Spaces for Developers: Why Your Eyes Disagree With The Math&lt;/a&gt; — Michael Lip — perceptual uniformity, relative colour syntax (&lt;code&gt;oklch(from var(…) …)&lt;/code&gt;), &lt;code&gt;color-mix&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/beefedai/designing-accessible-color-systems-and-ensuring-contrast-across-themes-2i43"&gt;Designing accessible color systems and ensuring contrast across themes&lt;/a&gt; — Beefed AI — semantic tokens, OKLCH, &lt;code&gt;prefers-color-scheme&lt;/code&gt;, &lt;code&gt;prefers-contrast&lt;/code&gt; / forced-colors in the test matrix, Style Dictionary handoff&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/matfrana/the-mystery-of-tailwind-colors-v4-hjh"&gt;The Mystery of Tailwind Colors (v4)&lt;/a&gt; — Mat Frana — practical OKLCH palette rebuild (lightness/chroma curves, P3 gamut); migration narrative rather than Token Studio/APCA&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/alexandersstudi/building-a-figma-to-github-token-pipeline-that-actually-works-3o0o"&gt;Building a Figma to GitHub token pipeline that actually works&lt;/a&gt; — Alexander's Studio — W3C &lt;code&gt;$value&lt;/code&gt; / &lt;code&gt;$type&lt;/code&gt; JSON → Style Dictionary → CSS variables (useful context even when Symfony owns emission)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Symfony-adjacent (same author, typography chain):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/font-manager-multi-format-font-export-for-symfony-41le"&gt;Font Manager: Multi-Format Font Export for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  From CSS files to Symfony infrastructure
&lt;/h2&gt;

&lt;p&gt;Node-centric pipelines (Style Dictionary, Tailwind config, theme JSON → build step) fit design agencies. They hurt Symfony teams who want &lt;strong&gt;Flex install, YAML config, and AssetMapper&lt;/strong&gt; without a second repository for tokens.&lt;/p&gt;

&lt;p&gt;UI Kernel inverts the default:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tokens live as data&lt;/strong&gt; — W3C DTCG-shaped theme layers (colour, spacing, radius, motion, typography references).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP resolves and validates&lt;/strong&gt; — lineage (Balanced, Semantic, Utility), light/dark variants, OKLCH ramp math, contrast-aware semantic colours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS is emitted server-side&lt;/strong&gt; — custom properties on &lt;code&gt;[data-theme="…"]&lt;/code&gt;, wrapped in cascade layers (&lt;code&gt;kernel.tokens&lt;/code&gt;, &lt;code&gt;kernel.profile&lt;/code&gt;) for predictable stacking with &lt;code&gt;ux-blocks-*&lt;/code&gt; tiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twig wires the page&lt;/strong&gt; — boot script, layer order, and theme CSS in the layout; no Encore entry for "theme variables."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Minimal layout (order matters — boot script first avoids a flash of the wrong theme):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="c"&gt;{# templates/base.html.twig #}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ui_kernel_theme_boot_script&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
    &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ui_kernel_layer_order&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
    &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ui_kernel_css&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the bundle partial, which emits the same three calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="s1"&gt;'@UiKernel/_head.html.twig'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration stays in familiar Symfony paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/packages/symfinity_ui_kernel.yaml&lt;/span&gt;
&lt;span class="na"&gt;symfinity_ui_kernel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default_theme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;semantic&lt;/span&gt;
    &lt;span class="na"&gt;default_variant&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;semantic&lt;/span&gt;
    &lt;span class="na"&gt;schema_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install is Composer + Flex — same story as any other bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require symfinity/ui-kernel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you do not need for theming:&lt;/strong&gt; Tailwind as the token source of truth, a Sass pipeline for variables, or Encore/Vite solely to compile &lt;code&gt;:root { --… }&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you still use Symfony for:&lt;/strong&gt; AssetMapper for component JS/CSS paths, Stimulus for micro-interactions, UX Twig Components for leaves — AssetMapper is &lt;strong&gt;plumbing&lt;/strong&gt;, not your design system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtime theme engine (0.3.x)
&lt;/h3&gt;

&lt;p&gt;Static token emission was the &lt;strong&gt;0.2.x&lt;/strong&gt; story. &lt;strong&gt;0.3.x&lt;/strong&gt; adds a theme &lt;em&gt;engine&lt;/em&gt; on top — still server-resolved, still no Node build step.&lt;/p&gt;

&lt;h4&gt;
  
  
  Runtime switching
&lt;/h4&gt;

&lt;p&gt;Users pick light, dark, or auto without redeploying CSS. The boot script sets &lt;code&gt;data-theme&lt;/code&gt; before first paint; preference cookies (&lt;code&gt;symfinity_ui_kernel_lineage&lt;/code&gt;, &lt;code&gt;symfinity_ui_kernel_scheme&lt;/code&gt;) persist the choice.&lt;/p&gt;

&lt;h4&gt;
  
  
  Scheme API
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;PATCH /_ui/theme/scheme&lt;/code&gt; returns resolved theme id, &lt;code&gt;colorScheme&lt;/code&gt;, and refreshed CSS when the client needs server-side dark-mode resolution.&lt;/p&gt;

&lt;h4&gt;
  
  
  Theme shell
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ui_kernel_theme_shell()&lt;/code&gt; emits layout chrome (lineage + light/dark/auto controls) wired to the same preference model.&lt;/p&gt;

&lt;h4&gt;
  
  
  Overlays
&lt;/h4&gt;

&lt;p&gt;Config &lt;code&gt;user_tokens&lt;/code&gt; merge atop the registry theme; app &lt;code&gt;config/themes/{lineage}/&lt;/code&gt; overrides bundle lineages; optional session preview on designated preview hosts (pairs with &lt;strong&gt;symfinity/ui-designer&lt;/strong&gt; in dev).&lt;/p&gt;

&lt;h4&gt;
  
  
  Theme inheritance
&lt;/h4&gt;

&lt;p&gt;Optional &lt;code&gt;extends&lt;/code&gt; on a variant entry in &lt;code&gt;theme.meta.yaml&lt;/code&gt; merges a parent theme layer before child overrides — multi-hop chains with cycle detection (&lt;strong&gt;0.3.1&lt;/strong&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Capability probe
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ui_kernel_theme_capabilities()&lt;/code&gt; reports which engine features are active (&lt;code&gt;runtime_switch&lt;/code&gt;, &lt;code&gt;user_token_overlay&lt;/code&gt;, &lt;code&gt;nested_theme&lt;/code&gt;, and others).&lt;/p&gt;

&lt;p&gt;Built-in lineages still ship as W3C DTCG files under &lt;code&gt;config/themes/{lineage}/&lt;/code&gt; (&lt;code&gt;theme.meta.yaml&lt;/code&gt; + &lt;code&gt;{variant}.dtcg.yaml&lt;/code&gt;), not a bespoke preset blob. Consumer apps drop overrides in the same layout; merged variants appear in &lt;code&gt;ThemeRegistry&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Authoring UI for composition graphs, import/export, and the visual studio at &lt;code&gt;/ui-designer&lt;/code&gt; lives in &lt;strong&gt;symfinity/ui-designer&lt;/strong&gt; (dev-only, requires ui-kernel ^0.3.1) — the kernel stays the runtime contract.&lt;/p&gt;




&lt;h2&gt;
  
  
  How this stays scalable
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Semantic tokens, not utility soup
&lt;/h3&gt;

&lt;p&gt;Components and UX Blocks reference &lt;strong&gt;roles and semantic variables&lt;/strong&gt; (&lt;code&gt;data-ui-role&lt;/code&gt;, &lt;code&gt;--ui-color-accent&lt;/code&gt;, spacing rhythm) — not &lt;code&gt;p-4 text-gray-600&lt;/code&gt; copied across Twig files.&lt;/p&gt;

&lt;p&gt;The kernel owns the &lt;strong&gt;look spine&lt;/strong&gt;; &lt;code&gt;symfinity/ux-blocks-*&lt;/code&gt; packages own &lt;strong&gt;component CSS&lt;/strong&gt; that consumes those variables. Boundary stays explicit: kernel emits tokens and global profile rules inside &lt;code&gt;@layer kernel.*&lt;/code&gt;; blocks emit role selectors inside &lt;code&gt;@layer blocks.*&lt;/code&gt;. Call &lt;code&gt;ui_kernel_layer_order()&lt;/code&gt; before theme CSS so integrator CSS can override predictably.&lt;/p&gt;

&lt;h3&gt;
  
  
  One theme graph, many surfaces
&lt;/h3&gt;

&lt;p&gt;The same resolved token graph can feed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web layouts&lt;/strong&gt; — static emission plus &lt;strong&gt;0.3.x&lt;/strong&gt; runtime switching, overlays, and inheritance without forking &lt;code&gt;app.css&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Email HTML with inline-safe subsets (horizon)&lt;/li&gt;
&lt;li&gt;PDF/print (horizon)&lt;/li&gt;
&lt;li&gt;CLI/TUI structured output (horizon)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the payoff of server-resolved tokens versus "whatever landed in &lt;code&gt;public/build/app.css&lt;/code&gt; last Tuesday."&lt;/p&gt;

&lt;h3&gt;
  
  
  OKLCH inside, CSS outside
&lt;/h3&gt;

&lt;p&gt;Internally, Symfinity generates palettes in &lt;strong&gt;OKLCH&lt;/strong&gt; for perceptual ramps and ref resolution. Public CSS still ships browser-ready strings (sRGB hex/&lt;code&gt;rgb()&lt;/code&gt;, with P3 where supported). You get palette math without asking every integrator to hand-author &lt;code&gt;oklch()&lt;/code&gt; literals in YAML.&lt;/p&gt;

&lt;h3&gt;
  
  
  Works with what you already run
&lt;/h3&gt;

&lt;p&gt;UI Kernel &lt;strong&gt;complements&lt;/strong&gt; Symfony UX — it does not replace Live Components, Turbo, or Stimulus.&lt;/p&gt;

&lt;p&gt;Typical Stage A path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install kernel for tokens on the layout shell.&lt;/li&gt;
&lt;li&gt;Drop &lt;strong&gt;UX Blocks&lt;/strong&gt; into existing Twig.&lt;/li&gt;
&lt;li&gt;Add &lt;strong&gt;font-manager&lt;/strong&gt; when typography tokens need real webfonts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plain Twig + your own CSS remains valid for marketing pages. The stack targets &lt;strong&gt;long-lived product and admin UI&lt;/strong&gt; where theme drift hurts.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Tailwind or Encore still make sense
&lt;/h2&gt;

&lt;p&gt;Honest scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind&lt;/strong&gt; — excellent for marketing microsites, rapid prototypes, or teams already standardized on utility CSS. Symfinity does not ask you to rip it out on day one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encore/Vite&lt;/strong&gt; — still right when you need a heavy JS application bundle, legacy React/Vue islands, or org-wide frontend tooling unrelated to tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UI Kernel draws a line: &lt;strong&gt;theming and design-system variables are Symfony infrastructure&lt;/strong&gt;, not npm devDependencies.&lt;/p&gt;

&lt;p&gt;If your pain is "every client has a different primary colour and our admin UI looks nothing like last year's project," tokens in PHP beat another &lt;code&gt;tailwind.config.js&lt;/code&gt; fork.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it locally
&lt;/h2&gt;

&lt;p&gt;After adding the &lt;a href="https://github.com/symfinity/recipes" rel="noopener noreferrer"&gt;symfinity/recipes&lt;/a&gt; Flex endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require symfinity/ui-kernel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handbook: &lt;a href="https://github.com/symfinity/ui-kernel/tree/main/docs" rel="noopener noreferrer"&gt;ui-kernel docs&lt;/a&gt; — &lt;a href="https://github.com/symfinity/ui-kernel/blob/main/docs/quickstart.md" rel="noopener noreferrer"&gt;quick start&lt;/a&gt;, &lt;a href="https://github.com/symfinity/ui-kernel/blob/main/docs/themes.md" rel="noopener noreferrer"&gt;themes (0.3.x)&lt;/a&gt;, &lt;a href="https://github.com/symfinity/ui-kernel/blob/main/docs/upgrade.md" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;UI Kernel roadmap:&lt;/strong&gt; &lt;strong&gt;0.3.x&lt;/strong&gt; theme engine (current) → &lt;strong&gt;0.4.x&lt;/strong&gt; token engine (computed tokens, references, expressions) → &lt;strong&gt;0.5.x&lt;/strong&gt; generator engine. See &lt;a href="https://github.com/symfinity/ui-kernel/blob/main/ROADMAP.md" rel="noopener noreferrer"&gt;ROADMAP.md&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't miss the introduction series to Symfinity:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/serotoninja/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-4nc1"&gt;Why I started building Symfony-native packages instead of doing infrastructure again and again&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/serotoninja/why-symfony-projects-feel-more-fragmented-than-ever-4g6c"&gt;Why Symfony projects feel more fragmented than ever&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/serotoninja/the-rule-behind-every-symfinity-package-gkh"&gt;The rule behind every Symfinity package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For package-level deep dives already published:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/font-manager-multi-format-font-export-for-symfony-41le"&gt;Font Manager: Multi-Format Font Export for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/omnia-ipsum-unified-placeholder-content-for-symfony-4oid"&gt;Omnia Ipsum: Unified Placeholder Content for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Articles on further Symfinity package tiers are planned — &lt;strong&gt;ui-designer&lt;/strong&gt; (theme authoring studio) and UX Blocks deep dives are on the list.&lt;/p&gt;

&lt;p&gt;Explore packages and source at &lt;a href="https://github.com/symfinity/" rel="noopener noreferrer"&gt;github.com/symfinity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Updated 2026-07-05 for ui-kernel 0.3.x (runtime switching, cascade layers, theme inheritance).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>symfony</category>
      <category>design</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>The rule behind every Symfinity package</title>
      <dc:creator>Wolf</dc:creator>
      <pubDate>Wed, 24 Jun 2026 20:00:14 +0000</pubDate>
      <link>https://dev.to/symfinity/the-rule-behind-every-symfinity-package-gkh</link>
      <guid>https://dev.to/symfinity/the-rule-behind-every-symfinity-package-gkh</guid>
      <description>&lt;p&gt;Whenever I start a new package, I try to follow a simple rule.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a package does not improve developer experience, it probably should not exist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sounds obvious. But it has become the guiding principle behind Symfinity.&lt;/p&gt;




&lt;h2&gt;
  
  
  A package must solve a real&amp;nbsp;problem
&lt;/h2&gt;

&lt;p&gt;The first question is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Would I actually use this package in my next project?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, it probably belongs in an experiment repository instead.&lt;/p&gt;

&lt;p&gt;That is how &lt;a href="https://github.com/symfinity/symfinity" rel="noopener noreferrer"&gt;symfinity/font-manager&lt;/a&gt; and &lt;a href="https://github.com/symfinity/symfinity" rel="noopener noreferrer"&gt;symfinity/omnia-ipsum&lt;/a&gt; started: I needed fonts and placeholder content on the next greenfield, not another abstract slot in a roadmap slide.&lt;/p&gt;




&lt;h2&gt;
  
  
  A package must be Symfony-native
&lt;/h2&gt;

&lt;p&gt;I do not want packages that fight Symfony. I want packages that feel like they belong there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symfony-native&lt;/strong&gt;, for Symfinity, means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composer-first&lt;/strong&gt; — install with &lt;code&gt;composer require&lt;/code&gt;, not a parallel plugin manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flex recipes where they earn their keep&lt;/strong&gt; — sensible defaults, manifest-driven wiring, overrides in the usual Symfony config paths&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard bundle shape&lt;/strong&gt; — Extension, Configuration, services tagged the Symfony way; no hidden bootstrap magic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks you already know&lt;/strong&gt; — Twig, Console, AssetMapper, and HTTP integration that follow Symfony docs, not a second framework inside the framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable integration&lt;/strong&gt; — if you know Symfony, you know where to look&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal surprises. No ecosystem lock-in dressed up as convenience.&lt;/p&gt;




&lt;h2&gt;
  
  
  A package must require little configuration
&lt;/h2&gt;

&lt;p&gt;Configuration is sometimes necessary.&lt;/p&gt;

&lt;p&gt;Excessive configuration is usually a sign that something can be improved.&lt;/p&gt;

&lt;p&gt;The best configuration is often the one users never have to write.&lt;/p&gt;




&lt;h2&gt;
  
  
  A package must work&amp;nbsp;alone
&lt;/h2&gt;

&lt;p&gt;Every Symfinity package should provide value on its own.&lt;/p&gt;

&lt;p&gt;If somebody installs only one package, the experience should still be worthwhile.&lt;/p&gt;

&lt;p&gt;No ecosystem lock-in. No artificial dependencies.&lt;/p&gt;

&lt;p&gt;Install only &lt;a href="https://github.com/symfinity/font-manager" rel="noopener noreferrer"&gt;symfinity/font-manager&lt;/a&gt; or only &lt;a href="https://github.com/symfinity/omnia-ipsum" rel="noopener noreferrer"&gt;symfinity/omnia-ipsum&lt;/a&gt; and you should still get a complete answer to that one problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  A package should work better&amp;nbsp;together
&lt;/h2&gt;

&lt;p&gt;At the same time, packages should share conventions.&lt;/p&gt;

&lt;p&gt;The more packages you combine, the more benefits you should get.&lt;/p&gt;

&lt;p&gt;Not because they are tightly coupled.&lt;/p&gt;

&lt;p&gt;Because they speak the same language.&lt;/p&gt;




&lt;h2&gt;
  
  
  The principle
&lt;/h2&gt;

&lt;p&gt;If I had to summarize the philosophy behind Symfinity in a single sentence, it would be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Independent packages. Shared conventions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That idea influences every design decision.&lt;/p&gt;

&lt;p&gt;And it will continue to guide the project as it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;This article is the third part of a short introduction series to Symfinity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/serotoninja/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-4nc1"&gt;Why building Symfony-native packages instead of doing infrastructure again and again&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/serotoninja/why-symfony-projects-feel-more-fragmented-than-ever-4g6c"&gt;Why Symfony projects feel more fragmented than ever&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For package-level deep dives already published:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/font-manager-multi-format-font-export-for-symfony-41le"&gt;Font Manager: Multi-Format Font Export for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/omnia-ipsum-unified-placeholder-content-for-symfony-4oid"&gt;Omnia Ipsum: Unified Placeholder Content for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Articles on further Symfinity package tiers are planned, this is just the beginning.&lt;/p&gt;

&lt;p&gt;Explore packages and source at &lt;a href="https://github.com/symfinity/" rel="noopener noreferrer"&gt;github.com/symfinity&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>softwaredevelopment</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why Symfony projects feel more fragmented than ever</title>
      <dc:creator>Wolf</dc:creator>
      <pubDate>Wed, 24 Jun 2026 19:49:28 +0000</pubDate>
      <link>https://dev.to/symfinity/why-symfony-projects-feel-more-fragmented-than-ever-4g6c</link>
      <guid>https://dev.to/symfinity/why-symfony-projects-feel-more-fragmented-than-ever-4g6c</guid>
      <description>&lt;p&gt;Let me start with something important.&lt;/p&gt;

&lt;h2&gt;
  
  
  I love Symfony
&lt;/h2&gt;

&lt;p&gt;In my opinion, Symfony remains one of the best foundations for serious PHP applications.&lt;/p&gt;

&lt;p&gt;So this is not criticism of Symfony. It is an observation about modern projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Symfony Is Great at Solving Individual Problems
&lt;/h2&gt;

&lt;p&gt;Symfony gives us excellent building blocks—routing, dependency injection, security, forms, Messenger, Twig, the UX initiative, AssetMapper, Flex recipes, and more.&lt;/p&gt;

&lt;p&gt;The ecosystem is huge and mature. That is a strength.&lt;/p&gt;

&lt;p&gt;And Symfony keeps improving the glue: Flex recipes wire bundles in predictably, AssetMapper reduces frontend build-tool sprawl for many apps, and the UX packages give us shared patterns for live components and Stimulus.&lt;/p&gt;

&lt;p&gt;Those tools genuinely reduce fragmentation &lt;strong&gt;within&lt;/strong&gt; Symfony.&lt;/p&gt;




&lt;h2&gt;
  
  
  The side effect of&amp;nbsp;success
&lt;/h2&gt;

&lt;p&gt;Even with better defaults, modern Symfony projects often still stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twig Components and UX packages from different vendors&lt;/li&gt;
&lt;li&gt;Asset management and font handling from different ecosystems&lt;/li&gt;
&lt;li&gt;Design systems and documentation tooling with their own conventions&lt;/li&gt;
&lt;li&gt;Frontend build tooling where AssetMapper is not enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each piece solves a legitimate problem. But the overall picture becomes harder to understand. The project starts to feel like a collection of systems rather than a single system.&lt;/p&gt;

&lt;p&gt;Placeholder content might come from one package, fonts from a workflow that still touches npm, UI primitives from a third library—each fine on its own, none sharing install steps or naming.&lt;/p&gt;




&lt;h2&gt;
  
  
  Death by configuration
&lt;/h2&gt;

&lt;p&gt;The challenge is rarely one specific configuration file. The challenge is understanding where everything lives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which configuration belongs to which package?&lt;/li&gt;
&lt;li&gt;What is a default?&lt;/li&gt;
&lt;li&gt;What is overridden?&lt;/li&gt;
&lt;li&gt;What came from a recipe?&lt;/li&gt;
&lt;li&gt;What came from a bundle?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more moving parts a project has, the more difficult those questions become.&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking for Consistency
&lt;/h2&gt;

&lt;p&gt;What I wanted was not less flexibility. I wanted more consistency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same conventions.&lt;/li&gt;
&lt;li&gt;The same installation experience.&lt;/li&gt;
&lt;li&gt;The same developer experience.&lt;/li&gt;
&lt;li&gt;The same language across multiple concerns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Symfony supplies the building blocks and much of the integration machinery.&lt;/p&gt;

&lt;p&gt;What was still missing for me was a &lt;strong&gt;cross-cutting layer&lt;/strong&gt;: packages that solve recurring app needs — fonts, placeholders, UI foundations — while agreeing on how they install, configure, and compose.&lt;/p&gt;

&lt;p&gt;That is a different job from replacing Flex or AssetMapper.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Symfinity Perspective
&lt;/h2&gt;

&lt;p&gt;Symfinity is my attempt to create that consistency layer.&lt;/p&gt;

&lt;p&gt;Not by replacing Symfony. Not by hiding Symfony.&lt;/p&gt;

&lt;p&gt;But by building packages that share common conventions and integrate naturally with each other — and with the Symfony tools you already use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Symfony's flexibility is one of its greatest strengths.&lt;/p&gt;

&lt;p&gt;But flexibility also creates opportunities for fragmentation—especially &lt;strong&gt;across&lt;/strong&gt; packages from different authors and eras.&lt;/p&gt;

&lt;p&gt;The goal of Symfinity is not to remove flexibility.&lt;/p&gt;

&lt;p&gt;The goal is to reduce friction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;This article is the second part of a short introduction series to Symfinity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/serotoninja/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-4nc1"&gt;Why building Symfony-native packages instead of doing infrastructure again and again&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/serotoninja/the-rule-behind-every-symfinity-package-gkh"&gt;The rule behind every Symfinity package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For package-level deep dives already published:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/font-manager-multi-format-font-export-for-symfony-41le"&gt;Font Manager: Multi-Format Font Export for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/omnia-ipsum-unified-placeholder-content-for-symfony-4oid"&gt;Omnia Ipsum: Unified Placeholder Content for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Articles on UI Kernel and the &lt;code&gt;ux-blocks-*&lt;/code&gt; component tiers are planned.&lt;/p&gt;

&lt;p&gt;Explore packages and source at &lt;a href="https://github.com/symfinity/" rel="noopener noreferrer"&gt;github.com/symfinity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was previously published on &lt;a href="https://medium.com/@serotoninja/why-symfony-projects-feel-more-fragmented-than-ever-eec6f7573e33" rel="noopener noreferrer"&gt;medium.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>softwaredevelopment</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why building Symfony-native packages instead of doing infrastructure again and again</title>
      <dc:creator>Wolf</dc:creator>
      <pubDate>Wed, 24 Jun 2026 18:58:50 +0000</pubDate>
      <link>https://dev.to/symfinity/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-4nc1</link>
      <guid>https://dev.to/symfinity/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-4nc1</guid>
      <description>&lt;p&gt;For years, I did what many Symfony developers do. Whenever I needed a feature, I searched for a package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need fonts? Go to Google Fonts and wire it up in the assets.&lt;/li&gt;
&lt;li&gt;Need placeholder content? Add a package, implement helpers, call in Twig.&lt;/li&gt;
&lt;li&gt;Need a frontend toolkit? Make a choice and wire it up in the assets and in Twig.&lt;/li&gt;
&lt;li&gt;Need documentation tooling? Add another package and implement again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these decisions were wrong. Most of the tools were excellent. But after building project after project, I noticed a pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same infrastructure, again and&amp;nbsp;again
&lt;/h2&gt;

&lt;p&gt;Every new project started with roughly the same checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;Placeholder content&lt;/li&gt;
&lt;li&gt;Assets&lt;/li&gt;
&lt;li&gt;UI helpers&lt;/li&gt;
&lt;li&gt;Documentation tooling&lt;/li&gt;
&lt;li&gt;Project conventions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The business logic was always different. The infrastructure was usually the same. Yet I kept rebuilding or reconfiguring it.&lt;/p&gt;

&lt;p&gt;Fonts often meant a separate frontend toolchain on top of PHP. Placeholder content meant yet another API shape and Twig integration to wire up. Each choice was reasonable; together they ate the first days of every greenfield.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Symfony stops being the whole story
&lt;/h2&gt;

&lt;p&gt;Symfony itself is fantastic.&lt;/p&gt;

&lt;p&gt;Routing, Dependency Injection, Security, Messenger, Twig, Console, Events — these are not the problem.&lt;/p&gt;

&lt;p&gt;The problem starts when a project grows.&lt;/p&gt;

&lt;p&gt;Soon there is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Composer&lt;/li&gt;
&lt;li&gt;npm&lt;/li&gt;
&lt;li&gt;Build tools&lt;/li&gt;
&lt;li&gt;Frontend frameworks&lt;/li&gt;
&lt;li&gt;Asset pipelines&lt;/li&gt;
&lt;li&gt;Configuration files everywhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing is broken.&lt;/p&gt;

&lt;p&gt;But the cognitive load keeps growing.&lt;/p&gt;

&lt;h2&gt;
  
  
  A different idea
&lt;/h2&gt;

&lt;p&gt;At some point I stopped asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Which dependency should I add next?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And started asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Why am I solving the same infrastructure problem for the tenth time?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question eventually became Symfinity. Not as a framework. Not as a Symfony replacement. Just as a collection of Symfony-native solutions for recurring problems.&lt;/p&gt;

&lt;p&gt;The first packages I packaged for real use were practical ones: &lt;a href="https://github.com/symfinity/symfinity" rel="noopener noreferrer"&gt;symfinity/font-manager&lt;/a&gt; for multi-format font export without leaving Composer and AssetMapper, and &lt;a href="https://github.com/symfinity/symfinity" rel="noopener noreferrer"&gt;symfinity/omnia-ipsum&lt;/a&gt; for placeholder content with one Twig-facing API instead of ad hoc fixtures.&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal
&lt;/h2&gt;

&lt;p&gt;Every package should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solve a real problem&lt;/li&gt;
&lt;li&gt;Feel native to Symfony&lt;/li&gt;
&lt;li&gt;Require minimal configuration&lt;/li&gt;
&lt;li&gt;Work independently&lt;/li&gt;
&lt;li&gt;Integrate naturally with other Symfinity packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A package should be useful even if it is the only Symfinity package in a project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Symfinity did not start with a grand vision of building an ecosystem. It started with a simple observation: I was rebuilding the same infrastructure over and over again. Eventually, packaging those solutions became the more sensible option.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;This article is the first part of a short introduction series to Symfinity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/serotoninja/why-symfony-projects-feel-more-fragmented-than-ever-4g6c"&gt;Why Symfony projects feel more fragmented than ever&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/serotoninja/the-rule-behind-every-symfinity-package-gkh"&gt;The rule behind every Symfinity package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For package-level deep dives already published:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/font-manager-multi-format-font-export-for-symfony-41le"&gt;Font Manager: Multi-Format Font Export for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/omnia-ipsum-unified-placeholder-content-for-symfony-4oid"&gt;Omnia Ipsum: Unified Placeholder Content for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Articles on further Symfinity package tiers are planned, this is just the beginning.&lt;/p&gt;

&lt;p&gt;Explore packages and source at &lt;a href="https://github.com/symfinity/" rel="noopener noreferrer"&gt;github.com/symfinity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was previously published on &lt;a href="https://medium.com/@serotoninja/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-6f5c7e9f425d" rel="noopener noreferrer"&gt;medium.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>php</category>
      <category>opensource</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Omnia Ipsum: Unified placeholder content for Symfony</title>
      <dc:creator>Wolf</dc:creator>
      <pubDate>Wed, 24 Jun 2026 18:16:39 +0000</pubDate>
      <link>https://dev.to/symfinity/omnia-ipsum-unified-placeholder-content-for-symfony-4oid</link>
      <guid>https://dev.to/symfinity/omnia-ipsum-unified-placeholder-content-for-symfony-4oid</guid>
      <description>&lt;h2&gt;
  
  
  Rethinking fake content in Symfony&amp;nbsp;projects
&lt;/h2&gt;

&lt;p&gt;A prototype web page displaying pure placeholder content&lt;br&gt;
When building early UI prototypes or shaping design systems in Symfony, placeholder content becomes a constant companion. Lorem ipsum text. Dummy profile photos. Placeholder videos. Silent audio. Temporary avatars. Realistic fake user data. Every project needs them — and yet most setups rely on a patchwork of libraries, links and hardcoded values.&lt;/p&gt;

&lt;p&gt;Omnia Ipsum aims to fix that by giving Symfony developers a single, elegant toolkit for placeholder content of all kinds.&lt;/p&gt;

&lt;p&gt;In this article, I will walk you through the motivation behind the project, the conceptual patterns it follows, and its most advanced features — all designed to make your prototyping workflow faster, cleaner and more maintainable.&lt;/p&gt;


&lt;h2&gt;
  
  
  Motivation: Why a placeholder library?
&lt;/h2&gt;

&lt;p&gt;Most Symfony projects start the same way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You add lorem ipsum text manually into Twig templates.&lt;/li&gt;
&lt;li&gt;You grab placeholder images from an external service.&lt;/li&gt;
&lt;li&gt;You generate avatars using yet another site.&lt;/li&gt;
&lt;li&gt;You paste in temporary YouTube or stock video URLs.&lt;/li&gt;
&lt;li&gt;You install Faker separately whenever realistic data is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is inconsistent, fragmented and difficult to maintain.&lt;br&gt;
And even worse: placeholder content often leaks into production unless guarded carefully.&lt;/p&gt;

&lt;p&gt;The idea behind Omnia Ipsum was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If your UI needs placeholder content, it should come from one place — predictable, configurable, and accessible directly from Twig.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This cuts down on boilerplate, cognitive overhead, and the "temporary chaos" of early-stage templates.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Prerequisite
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/symfinity/recipes" rel="noopener noreferrer"&gt;github.com/symfinity/recipes&lt;/a&gt; and follow the instructions to add the required recipe repository.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; symfinity/omnia-ipsum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;Use the Twig functions immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Placeholder"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_avatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Avatar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;video&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_video&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/video&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;audio&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/audio&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;lorem_title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;lorem_paragraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt; – &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fake_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Everything is a Twig function
&lt;/h2&gt;

&lt;p&gt;Omnia Ipsum provides a set of expressive Twig helpers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;omnia_image(width, height)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;omnia_avatar(name, size)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;omnia_video(width, height)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;omnia_audio(seconds)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lorem_paragraphs(count)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fake('email')&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fake_text(200)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advanced Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multiple Image Providers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'picsum'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'dummy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'dummyimage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;background&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'#3355ff'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatic Avatar Generation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;omnia_avatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Alice Johnson'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Placeholder Video &amp;amp; Audio
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;video&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ omnia_video(1920, 1080) }}"&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/video&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;audio&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ omnia_audio(5) }}"&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/audio&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Integrated Lorem Ipsum Utilities
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;lorem_title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;lorem_sentences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;lorem_paragraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Faker Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fake_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Extensible Architecture
&lt;/h2&gt;

&lt;p&gt;Developers can add custom providers or extend generator logic via Symfony services.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use Omnia Ipsum
&lt;/h2&gt;

&lt;p&gt;Ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design systems&lt;/li&gt;
&lt;li&gt;Component libraries&lt;/li&gt;
&lt;li&gt;Prototyping complex UIs&lt;/li&gt;
&lt;li&gt;Responsive layout testing&lt;/li&gt;
&lt;li&gt;Admin dashboards&lt;/li&gt;
&lt;li&gt;Workshops and trainings&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Omnia Ipsum provides a unified, elegant API for all placeholder content commonly needed in Symfony projects. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want cleaner templates, faster prototyping, and a more structured workflow, Omnia Ipsum is a strong addition to your toolbox.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Try it now: &lt;a href="https://github.com/symfinity/omnia-ipsum" rel="noopener noreferrer"&gt;symfinity/omnia-ipsum&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find it useful, star the repository and share it with your Symfony community.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Don't miss the introduction series to Symfinity:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/serotoninja/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-4nc1"&gt;Why building Symfony-native packages instead of doing infrastructure again and again&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/serotoninja/why-symfony-projects-feel-more-fragmented-than-ever-4g6c"&gt;Why Symfony projects feel more fragmented than ever&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/serotoninja/the-rule-behind-every-symfinity-package-gkh"&gt;The rule behind every Symfinity package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Another package-level deep dive:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/font-manager-multi-format-font-export-for-symfony-41le"&gt;Font Manager: Multi-Format Font Export for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Articles on further Symfinity package tiers are planned, this is just the beginning.&lt;/p&gt;

&lt;p&gt;Explore packages and source at &lt;a href="https://github.com/symfinity/" rel="noopener noreferrer"&gt;github.com/symfinity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was previously published on &lt;a href="https://medium.com/@serotoninja/omnia-ipsum-unified-placeholder-content-for-symfony-f8dfb1a89a6b" rel="noopener noreferrer"&gt;medium.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>php</category>
      <category>softwaredevelopment</category>
      <category>dev</category>
    </item>
    <item>
      <title>Font Manager: Multi-format Font export for Symfony</title>
      <dc:creator>Wolf</dc:creator>
      <pubDate>Wed, 24 Jun 2026 18:15:42 +0000</pubDate>
      <link>https://dev.to/symfinity/font-manager-multi-format-font-export-for-symfony-41le</link>
      <guid>https://dev.to/symfinity/font-manager-multi-format-font-export-for-symfony-41le</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Typography should be one of the simplest parts of a project.&lt;/p&gt;

&lt;p&gt;In reality, it often ends up scattered across multiple layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bootstrap: &lt;code&gt;$font-family-base&lt;/code&gt; variables&lt;/li&gt;
&lt;li&gt;Tailwind: JavaScript configuration&lt;/li&gt;
&lt;li&gt;TypeScript: type definitions&lt;/li&gt;
&lt;li&gt;Design systems: W3C Design Tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same font information gets copied and maintained in several places. Every update means touching multiple files, hoping everything stays in sync.&lt;/p&gt;

&lt;p&gt;It's repetitive, error-prone, and easy to get wrong.&lt;/p&gt;

&lt;p&gt;So I built Font Manager.&lt;/p&gt;

&lt;p&gt;Define your fonts once and export them in whatever format your project needs — CSS, Bootstrap variables, Tailwind configuration, TypeScript definitions, design tokens, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;A simple Twig function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;font_manager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ubuntu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'400 700'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;symfinity_font_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;scss_bootstrap&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tailwind_config&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;typescript_definitions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One lock command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php bin/console fonts:lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every format, automatically generated. Perfectly synced.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bootstrap Example
&lt;/h3&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Manually copy font name&lt;/span&gt;
&lt;span class="nv"&gt;$font-family-base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Ubuntu'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Duplication&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'bootstrap/scss/bootstrap'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;symfinity_font_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;scss_bootstrap&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php bin/console fonts:lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.scss&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'./assets/styles/fonts-bootstrap'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ← Auto-generated&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'bootstrap/scss/bootstrap'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bootstrap uses your fonts automatically. No manual mapping. No duplication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailwind Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;symfinity_font_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;tailwind_config&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tailwind.config.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fonts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./assets/fonts-tailwind.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ← Auto-generated&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-sans"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your custom font, via Tailwind.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  TypeScript Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;symfinity_font_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;typescript_definitions&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FontFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./assets/fonts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;applyFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sans&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// ✓ Valid&lt;/span&gt;
&lt;span class="nf"&gt;applyFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✗ TypeScript error!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typos caught at compile time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 12 Formats
&lt;/h2&gt;

&lt;p&gt;CSS, SCSS, JavaScript, Design Tokens — pick what you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php bin/console fonts:formats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSS: Variables, Modules, @layer&lt;br&gt;
SCSS: Variables, Bootstrap, Mixins&lt;br&gt;
JavaScript: ESM, Tailwind, TypeScript&lt;br&gt;
Design System: JSON, W3C Tokens, Figma, Style Dictionary&lt;/p&gt;
&lt;h2&gt;
  
  
  Build Tools
&lt;/h2&gt;

&lt;p&gt;AssetMapper, Webpack, or Vite? Auto-detected.&lt;/p&gt;

&lt;p&gt;Output paths adjust automatically. No config needed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Design Systems
&lt;/h2&gt;

&lt;p&gt;Export to Figma Tokens, Style Dictionary, or W3C Design Tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;design_tokens&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;figma_tokens&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx style-dictionary build  &lt;span class="c"&gt;# CSS, iOS, Android from one source&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Design once. Ship everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy Options
&lt;/h2&gt;

&lt;p&gt;Not just Google — choose your provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;symfinity_font_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default_provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bunny'&lt;/span&gt;  &lt;span class="c1"&gt;# GDPR-compliant, zero tracking&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google, Bunny, Fontsource, or Local. Same API. Your choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Architecture: Strategy pattern. Each format is a separate exporter.&lt;/p&gt;

&lt;p&gt;Dependency resolution: Automatic. css_modules needs css_variables? Handled.&lt;/p&gt;

&lt;p&gt;Build detection: Scans for webpack.config.js, vite.config.js, asset_mapper.yaml.&lt;/p&gt;

&lt;p&gt;Output paths: Adjusted per build tool. AssetMapper → assets/styles/. Webpack → assets/.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commands:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php bin/console fonts:formats           &lt;span class="c"&gt;# List all formats&lt;/span&gt;
php bin/console fonts:format:info scss  &lt;span class="c"&gt;# Usage instructions&lt;/span&gt;
php bin/console fonts:export            &lt;span class="c"&gt;# Export manually&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Migrating from google-fonts
&lt;/h3&gt;

&lt;p&gt;One command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php bin/console fonts:migrate-from-google-fonts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Config, templates, manifest — all updated automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/symfinity/recipes" rel="noopener noreferrer"&gt;github.com/symfinity/recipes&lt;/a&gt; and follow the instructions to add the required recipe repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require symfinity/font-manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. Symfony Flex does the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;font_manager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ubuntu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'400 700'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dev: CDN&lt;br&gt;
Prod: Self-hosted (after fonts:lock)&lt;/p&gt;

&lt;p&gt;Add exports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;symfinity_font_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;scss_bootstrap&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;tailwind_config&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lock:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php bin/console fonts:lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'./fonts-bootstrap'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'bootstrap/scss/bootstrap'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Typography shouldn’t waste your time or violate privacy law.&lt;br&gt;
With &lt;em&gt;font-manager&lt;/em&gt;, you can design freely in development, ship safely in production, and forget about fonts altogether.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One Twig function. Multiple providers. Twelve export formats. Zero manual mapping.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s Font Manager.&lt;/p&gt;

&lt;p&gt;Try it now: &lt;a href="https://github.com/symfinity/font-manager" rel="noopener noreferrer"&gt;symfinity/font-manager&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find it useful, star the repository and share it with your Symfony community.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Don't miss the introduction series to Symfinity:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/serotoninja/why-i-started-building-symfony-native-packages-instead-of-doing-infrastructure-again-and-again-4nc1"&gt;Why building Symfony-native packages instead of doing infrastructure again and again&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/serotoninja/why-symfony-projects-feel-more-fragmented-than-ever-4g6c"&gt;Why Symfony projects feel more fragmented than ever&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/serotoninja/the-rule-behind-every-symfinity-package-gkh"&gt;The rule behind every Symfinity package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Another package-level deep dive:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/serotoninja/omnia-ipsum-unified-placeholder-content-for-symfony-4oid"&gt;Omnia Ipsum: Unified Placeholder Content for Symfony&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Articles on further Symfinity package tiers are planned, this is just the beginning.&lt;/p&gt;

&lt;p&gt;Explore packages and source at &lt;a href="https://github.com/symfinity/" rel="noopener noreferrer"&gt;github.com/symfinity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was previously published on &lt;a href="https://medium.com/@serotoninja/beyond-css-multi-format-font-export-for-symfony-cabc2692734a" rel="noopener noreferrer"&gt;medium.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>symfony</category>
      <category>php</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
