<?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>When the pillars collapse one after another</title>
      <dc:creator>Wolf</dc:creator>
      <pubDate>Tue, 11 Aug 2026 09:50:22 +0000</pubDate>
      <link>https://dev.to/serotoninja/when-the-pillars-collapse-one-after-another-10mk</link>
      <guid>https://dev.to/serotoninja/when-the-pillars-collapse-one-after-another-10mk</guid>
      <description>&lt;p&gt;Most of what I write about here has something to do with software: systems, architecture, tools, failures, and the occasional attempt to understand why something that looked perfectly stable suddenly isn’t.&lt;/p&gt;

&lt;p&gt;This one is different.&lt;/p&gt;

&lt;p&gt;Over the past few months, several of the things I considered stable parts of my life have either disappeared or started to move at roughly the same time. Not all of them are technical problems. In fact, most of them cannot be fixed with a better abstraction, another test, or a carefully planned migration.&lt;/p&gt;

&lt;p&gt;Still, I noticed that I kept thinking about what was happening in the language I know best: systems, dependencies, redundancy, cascading failures, architecture and rebuilding.&lt;/p&gt;

&lt;p&gt;So this is not really a software article.&lt;/p&gt;

&lt;p&gt;But it might be an engineer’s way of thinking about what happens when the system in question is your own life.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when life does not collapse all at once, but loses its structural support one pillar at a time?
&lt;/h2&gt;

&lt;p&gt;There are things in life that we rarely think about as long as they work. A relationship, a career, a home, family, friendships, health, plans for the future. They form the structure around us so naturally that after a while we stop seeing them as separate things. Together, they simply become what we call my life.&lt;/p&gt;

&lt;p&gt;It is only when one of them disappears that we notice how much weight it was carrying.&lt;/p&gt;

&lt;p&gt;When that happens, the first reaction is usually not to question the whole structure. We compensate. If a relationship ends, work suddenly becomes more important. It provides routine, purpose, people, problems to solve and a reason to get up in the morning. If work becomes difficult, perhaps home and family become the safe place instead. If the future becomes uncertain, familiar routines keep the present predictable.&lt;/p&gt;

&lt;p&gt;In other words, we redistribute the load.&lt;/p&gt;

&lt;p&gt;As a software engineer, I cannot help seeing a familiar pattern in this. We design systems with the assumption that components will fail. A resilient system is not one in which nothing ever breaks; it is one that can tolerate something breaking without taking everything else down with it. We introduce redundancy, isolate failures and try very hard not to create single points of failure.&lt;/p&gt;

&lt;p&gt;Human lives work surprisingly similarly, except that nobody sits down at twenty-five and draws an architecture diagram for the next fifty years.&lt;/p&gt;

&lt;p&gt;We accumulate dependencies instead.&lt;/p&gt;

&lt;p&gt;A partner gradually becomes responsible for most of our emotional intimacy. An employer provides not only money but identity, structure, social contact and a sense of usefulness. A city becomes synonymous with home. Family represents continuity. A particular idea of the future gives meaning to decisions made in the present.&lt;/p&gt;

&lt;p&gt;None of this feels dangerous while everything is running.&lt;/p&gt;

&lt;p&gt;Then one component fails.&lt;/p&gt;

&lt;p&gt;And usually, the system survives.&lt;/p&gt;

&lt;p&gt;The interesting part begins when another one fails shortly afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  When failure stops being isolated
&lt;/h2&gt;

&lt;p&gt;There is a significant difference between having several problems and experiencing cascading failure.&lt;/p&gt;

&lt;p&gt;If one important part of life disappears, the remaining parts absorb some of its weight. That is one of the ways we recover. But it also means that those remaining structures suddenly matter more than they did before.&lt;/p&gt;

&lt;p&gt;After losing one source of stability, it is natural to think: at least I still have my work. At least I still have my family. At least I still know where I belong. At least this part of my future is certain.&lt;/p&gt;

&lt;p&gt;Those sentences are comforting because they define the boundary of the damage. Something has been lost, but the rest of the map still exists.&lt;/p&gt;

&lt;p&gt;If one of those remaining certainties then starts to disappear as well, the effect is strangely disproportionate. The second loss does not merely add another problem. It changes the meaning of the first one. What previously looked like damage to one part of life starts to look like uncertainty about the structure itself.&lt;/p&gt;

&lt;p&gt;The question changes from "How do I deal with this?" to "What exactly is still stable?".&lt;/p&gt;

&lt;p&gt;That is a much harder question.&lt;/p&gt;

&lt;p&gt;In engineering terms, redundancy only helps while redundant components remain independent. If one failure increases the load on another component, which then becomes more likely to fail, you have the beginning of a cascade.&lt;/p&gt;

&lt;p&gt;Life does not follow engineering diagrams, of course. A relationship is not a service, a family is not a cluster and an employer is not an availability zone. You cannot replicate your father across three data centers, and there is no sensible SLA for friendship.&lt;/p&gt;

&lt;p&gt;Still, the analogy exposes something that I find uncomfortable: many things I think of as parts of my identity are actually infrastructure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“I am a software engineer.”&lt;/li&gt;
&lt;li&gt;“I am someone’s partner.”&lt;/li&gt;
&lt;li&gt;“I work for this company.”&lt;/li&gt;
&lt;li&gt;“This city is my home.”&lt;/li&gt;
&lt;li&gt;“This is what I will probably be doing five years from now.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These sentences sound like descriptions of a person. But they also describe relationships between that person and external systems. Employers can disappear. Relationships can end. Cities can become unaffordable or unfamiliar. Families change. Bodies change. Plans become obsolete.&lt;/p&gt;

&lt;p&gt;When several of those things move at once, it can feel as if identity itself is being dismantled.&lt;/p&gt;

&lt;p&gt;Perhaps it is not.&lt;/p&gt;

&lt;p&gt;Perhaps what is being dismantled is the structure that made identity easy to describe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The urge to rebuild
&lt;/h2&gt;

&lt;p&gt;This is usually the point where advice starts arriving.&lt;/p&gt;

&lt;p&gt;Find a new job. Meet someone new. Move somewhere else. Start a project. Set goals. Establish routines. Reinvent yourself. Look forward instead of backward.&lt;/p&gt;

&lt;p&gt;There is nothing inherently wrong with any of that. Eventually, some form of rebuilding is necessary. Human beings need attachment, purpose, structure and other people. The goal cannot reasonably be to become independent of everything that might someday disappear.&lt;/p&gt;

&lt;p&gt;But I have started to wonder whether rebuilding too quickly has its own risk.&lt;/p&gt;

&lt;p&gt;If a building collapses and the first priority is to replace every missing wall exactly where it used to be, there is not much opportunity to ask whether the old floor plan was actually good.&lt;/p&gt;

&lt;p&gt;A new relationship can occupy exactly the same structural position as the old one. A new employer can become the same source of identity and security. A new five-year plan can restore the reassuring illusion that the future has agreed to cooperate.&lt;/p&gt;

&lt;p&gt;The materials change, but the architecture remains untouched.&lt;/p&gt;

&lt;p&gt;This is where the metaphor of an empty field becomes more useful to me than the metaphor of ruins.&lt;/p&gt;

&lt;p&gt;An empty field initially sounds bleak. There is no shelter, no obvious orientation and nothing telling you where anything belongs. If you have spent years maintaining a structure, the absence of one looks like failure.&lt;/p&gt;

&lt;p&gt;But an empty field has one property that a finished building does not have: it has not yet decided what it must become.&lt;/p&gt;

&lt;p&gt;That does not automatically make loss a gift. I dislike the tendency to turn every painful event into an inspirational lesson. Some losses are simply losses. Things that mattered are gone, and there is no obligation to be grateful for that.&lt;/p&gt;

&lt;p&gt;Freedom itself is not always pleasant either. Freedom that you deliberately choose can feel exhilarating; freedom produced by losing things you wanted to keep can feel remarkably similar to abandonment.&lt;/p&gt;

&lt;p&gt;And yet both can be true at once.&lt;/p&gt;

&lt;p&gt;An empty field can be frightening because nothing is there, and valuable because nothing is fixed.&lt;/p&gt;

&lt;p&gt;You do not have to thank the bulldozer to notice the horizon.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can be carried?
&lt;/h2&gt;

&lt;p&gt;Maybe, then, the first question after a collapse should not be "What do I build next?".&lt;/p&gt;

&lt;p&gt;Maybe it should be "What survived?".&lt;/p&gt;

&lt;p&gt;Not in the sense of searching for some mystical, immutable “true self” underneath everything else. I am not convinced such a thing exists. People are shaped by their relationships, environments, work and history. Remove all context from a person and there may not be some perfectly preserved essence waiting underneath.&lt;/p&gt;

&lt;p&gt;But some things are more portable than others.&lt;/p&gt;

&lt;p&gt;An employer can disappear, but skills travel. A project can end, but experience remains. Plans can fail without destroying curiosity. A relationship can end without erasing the capacity to love. Institutions can disappear while values remain. Creative work can survive circumstances that completely change the context in which it was created.&lt;/p&gt;

&lt;p&gt;Perhaps these things are not pillars at all. Pillars support us from outside. These are things we carry.&lt;/p&gt;

&lt;p&gt;That distinction seems increasingly important to me.&lt;/p&gt;

&lt;p&gt;The usual story about personal crisis has a satisfying narrative arc. Something collapses, the protagonist struggles, learns what really matters, rebuilds a better life and eventually looks backward from a position of renewed stability. It is a good structure for books, talks and films because it has an ending.&lt;/p&gt;

&lt;p&gt;Actual life has an inconvenient middle.&lt;/p&gt;

&lt;p&gt;There is a period in which you know what has ended but do not yet know what begins next. The old blueprint no longer applies, but there is no new one. There may be possibilities without preferences, freedom without direction and hope without anything concrete to attach it to.&lt;/p&gt;

&lt;p&gt;We seem strangely uncomfortable with that state. We treat uncertainty as a temporary defect that should be resolved as efficiently as possible.&lt;/p&gt;

&lt;p&gt;I am no longer sure that is always useful.&lt;/p&gt;

&lt;p&gt;There may be value in spending some time between buildings.&lt;/p&gt;

&lt;p&gt;Not indefinitely. Not as an excuse to withdraw from life. But long enough to look at the old structure without immediately trying to reproduce it.&lt;/p&gt;

&lt;p&gt;Which parts actually supported me? Which parts merely occupied space? Which compromises had quietly become load-bearing? Which rooms had I stopped entering years ago? Which dependencies were unavoidable, and which ones did I create because they made life easier to predict?&lt;/p&gt;

&lt;p&gt;And perhaps the most important question: if nobody handed me the old blueprint again, would I really design the same building?&lt;/p&gt;

&lt;p&gt;I do not have an answer yet.&lt;/p&gt;

&lt;p&gt;That is partly why I wanted to write this now rather than years later, when memory might have rearranged everything into a clean story about adversity and personal growth. I am more interested in the unresolved part: the point at which enough familiar structures have become uncertain that rebuilding the old life no longer seems like the only imaginable objective.&lt;/p&gt;

&lt;p&gt;Losing one pillar makes you reach for the others. Losing several can make you question the architecture.&lt;/p&gt;

&lt;p&gt;Maybe that does not mean everything has failed.&lt;/p&gt;

&lt;p&gt;Maybe it means there is finally a difference between rebuilding stability and rebuilding the past.&lt;/p&gt;

&lt;p&gt;If you ever find yourself standing on that empty field, there may be no reason to start pouring concrete immediately. You can stand there for a while. Look at what is still with you. Look at what you actually miss and what you only assumed you were supposed to replace.&lt;/p&gt;

&lt;p&gt;Then, eventually, decide what deserves to be built again.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article has also been published on &lt;a href="https://medium.com/@symfinity.net/when-the-pillars-collapse-one-after-another-f14917604762" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>learning</category>
      <category>career</category>
      <category>life</category>
      <category>future</category>
    </item>
    <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>
