[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f2ruJSGgap4OFeVXYCAZJN5dJymWqOIpZBFeyCMeFkYE":3,"$fpKy4L2tysV5RHkFURswz7l5dLEerMAtC_tZqyFQB_lY":8,"$f1iexrhCdX0D5Lrf4Pvk5Mdoh-jXnbR8tXxJMj7Joni0":37,"$fo8nUKzZZQ4Mrj2CyWrGqbYx330XkD75NwzlwAyN6vx0":236,"$fF4Od4qzPhvbKYsIxge6_BrCEZzPOoeLY10eWpZE9l_g":247},{"intro":4,"currentFocus":5,"location":6,"availability":7},"Senior developer and DevOps engineer based in Skanderborg, Denmark. Self-taught. 15 years building on Drupal, PHP, Docker, and Linux. Currently at Eksponent, a Danish development agency based in Copenhagen. \r\nAlso technical lead for South African History Online since 2018. Documentary photographer.","Headless Drupal, civic tech, self-hosted infrastructure","Skanderborg, Denmark","available",{"posts":9,"total":34,"page":35,"limit":36},[10,18,26],{"id":11,"title":12,"teaser":13,"body":14,"slug":15,"date":16,"tags":17},"45f9806c-6edd-44e7-9899-7c23daa2c137","Technology, history and who gets to control memory","I recently joined Hasanain Abdullah for a live conversation hosted through Ummah Tech. The subject was how technology can serve social change - specifically, how it can put a country's history into…","\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">I recently joined Hasanain Abdullah for a live conversation hosted through Ummah Tech. The subject was how technology can serve social change - specifically, how it can put a country's history into the hands of anyone who wants it, at no cost.&nbsp;\u003C\u002Fp>\u003Ch2 class=\"text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold\">What South African History Online is, and why it exists\u003C\u002Fh2>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">South African History Online, known as SAHO, was founded by the veteran documentary photographer Omar Badsha in the late 1990s and registered as a non-profit in 2000. It has since become the largest and most comprehensive online resource on South African and African history and culture, reaching millions of people every year and serving as a trusted reference for schools, universities, media and the general public. Its work has been recognised with the National Institute for the Humanities and Social Sciences award for digital humanities, and Badsha himself received the National Order of Ikhamanga for his contribution to preserving the country's history.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">The founding purpose is the important part. SAHO was built as a people's history project - to break the silence on the past and to challenge the biased account of African and South African heritage that dominated educational and cultural institutions for generations. The version of history most South Africans were handed in school was written from a narrow point of view. The contribution of struggle stalwarts to the liberation of the country, the depth of indigenous history, the ordinary life of communities that the state tried to erase - much of this sat outside the formal curriculum.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">That gap is not abstract. South African learners study history as part of the CAPS curriculum, and students continue that study at a tertiary level. An open archive that anyone can reach for free is a direct answer to a system where good historical material has too often sat behind paywalls, out of print, or inside institutions that most people cannot access. SAHO hosts major collections, links its articles to archival material and academic sources, and makes the whole thing free to read. Access is the entire point. An archive that cannot be reached does not exist.\u003C\u002Fp>\u003Ch2 class=\"text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold\">My part in it\u003C\u002Fh2>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">I have been Technical Lead for SAHO since 2010, on a volunteer basis. My involvement goes back far enough that I co-curated the Bonani Africa 2010 photography catalogue alongside Omar Badsha and Jeeva Rajgopaul. The history itself is the work of the editorial team. My job is the infrastructure that carries it - and I do not treat that as a footnote to the mission. It is the mission expressed in another language.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">SAHO runs on open-source frameworks, principally Drupal, self-hosted on infrastructure the project controls. Full-stack development work turns those open frameworks into an enterprise-grade platform capable of holding decades of accumulated material and serving it reliably at scale. The codebase is public, with a contribution guide, so that developers, designers and researchers can help improve it. Phoenix and I maintain the platforms through our company, Fenix Nordic Solutions, in support of the editorial team.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">The choice of open, self-hosted software is deliberate, and I will return to why below. A people's history project cannot sit on proprietary, paywalled, surveilled infrastructure without contradicting itself. The medium has to match the message.\u003C\u002Fp>\u003Ch2 class=\"text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold\">The history the curriculum left out: forced removals\u003C\u002Fh2>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">Much of what SAHO documents is displacement, and the clearest example is one that shaped the city I photographed for a decade. District Six was a dense, working-class, multi-cultural neighbourhood on the edge of the Cape Town city centre - a community of freed slaves, artisans, labourers, merchants and immigrants that had lived together, across race and religion, since the nineteenth century.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">On 11 February 1966 the apartheid government declared District Six a whites-only area under the Group Areas Act. Over the years that followed, more than sixty thousand people were forcibly removed and their homes flattened by bulldozers. They were resettled far out on the Cape Flats - in Mitchells Plain, Manenberg, Hanover Park, Langa, Gugulethu and elsewhere - according to how the state had racially classified them. The removals did not only take houses. They scattered extended families, broke the daily intimacy of a shared neighbourhood, and deepened the very racial separation the state was engineering. District Six, together with Sophiatown in Johannesburg, became a national and international symbol of the cruelty of forced removal.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">This is the kind of history that formal schooling has under-taught, and it is exactly the kind of history an open archive exists to hold. Forced removal is not only a physical act. It is an erasure of place and memory. When you demolish a neighbourhood, you demolish the record of the life that was lived there. An archive is what refuses that erasure.\u003C\u002Fp>\u003Ch2 class=\"text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold\">Behind the lens: photographing the afterlife of a policy\u003C\u002Fh2>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">My photography in Cape Town is, by and large, social documentation. The subjects shift - from the transport networks and systems around the city and the people and conversations inside them, to the youth of Mitchell's Plain and the challenges young people face in the country's oldest town. I have used the camera as a way of engaging with people and gathering stories that stand as a record of what took place.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">The transport work is where the history becomes visible in the present. The spatial geography of apartheid did not end in 1994. Townships built to hold the displaced still sit far from the centre - Khayelitsha is roughly thirty kilometres from the Cape Town CBD - and the people moved there still make long, costly, unreliable journeys to reach work. Studies of the city estimate that poor households can spend up to forty percent of their disposable income simply travelling to work, a burden that traps families in marginal locations and holds back any climb out of poverty. Cape Town carries the largest housing backlog of any South African metro and is widely regarded as the country's most segregated city. The collapse of commuter rail services on the Central Line made the daily journey harder still.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">So when I photograph a commute from the CBD out to Mitchells Plain or Khayelitsha, I am not photographing a neutral fact of city life. I am photographing the afterlife of the Group Areas Act - the daily movement that forced removal made necessary, still running decades later. That is social science in motion, in the most literal sense.\u003C\u002Fp>\u003Ch2 class=\"text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold\">Why open source, and why it matters beyond the code\u003C\u002Fh2>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">The technical choices behind SAHO are the mission expressed as engineering. Open source, self-hosting, open standards, a public codebase - these are the same instinct as free and open access to history. I advocate for open-source software, including running operating systems such as Ubuntu, because ownership of the tools is inseparable from ownership of the work.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">When historical knowledge lives on software that anyone can inspect, run and improve, and on infrastructure the project itself controls, the archive is not hostage to a vendor who could disappear, raise a price, or lock it behind a login tomorrow. When the sources are public, findable and cited, anyone can check the work. History behind a closed door asks you to trust it. History in the open asks you to verify it. I will take the second every time. This is digital sovereignty applied to memory, and it is the whole reason the platform is built the way it is.\u003C\u002Fp>\u003Ch2 class=\"text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold\">AI as leverage, not decoration\u003C\u002Fh2>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">Artificial intelligence has a place in this work, but a disciplined one. The archive is far too large to curate entirely by hand, so AI-assisted metadata enrichment lets us tag, structure and cross-link tens of thousands of entries at a scale manual effort could never reach. That serves discoverability directly - it helps a learner or a researcher find the exact thing they need. That is AI as leverage on a real problem.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">And I refuse it where it would flatten the work into sameness. The current redesign of SAHO is positioned deliberately outside the default look that AI image tools push you toward - it is archive-first, built on libre typefaces, and rooted in the material itself rather than a generic machine-made sheen. Use the tools where they multiply human effort. Refuse them where they would erase the character of the thing. That distinction is the point.\u003C\u002Fp>\u003Ch2 class=\"text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold\">The thread that ties it together\u003C\u002Fh2>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">Open source, self-hosting, open standards, and free access to history are not separate commitments. They are one instinct. Whoever controls the record controls the memory of a nation, and open technology is how you keep that control in the commons rather than handing it to a landlord. The archive resists the erasure that displacement causes. The photography documents that displacement's afterlife in the streets. The software keeps all of it free, verifiable, and out of anyone's private enclosure.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">My thanks to Hasanain for the conversation.\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">Watch the full session: \u003Ca class=\"underline underline-offset-2 decoration-1 decoration-current\u002F40 hover:decoration-current focus:decoration-current\" href=\"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=q_vI-hU64Bw\">https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=q_vI-hU64Bw\u003C\u002Fa>&nbsp;\u003C\u002Fp>\u003Cp class=\"font-claude-response-body break-words whitespace-normal\">Visit South African History Online: \u003Ca class=\"underline underline-offset-2 decoration-1 decoration-current\u002F40 hover:decoration-current focus:decoration-current\" href=\"https:\u002F\u002Fsahistory.org.za\u002F\">https:\u002F\u002Fsahistory.org.za\u002F\u003C\u002Fa>&nbsp;\u003C\u002Fp>","technology-history-and-who-gets-control-memory","2026-07-01T12:14:06+00:00",[],{"id":19,"title":20,"teaser":21,"body":22,"slug":23,"date":24,"tags":25},"94919d85-31f3-4847-a903-6f737c7b3f03","Two Pushes to drupal.madsnorgaard.net - Main and Security Updates Branch","Technical breakdown of two Git pushes to drupal.madsnorgaard.net: incremental updates to main and a security-focused branch for June 2026 patches in a headless Drupal 11\u002FNuxt 3 architecture.","I pushed commits to two branches of the drupal.madsnorgaard.net repository - one to main and one to a security-focused feature branch. Both represent maintenance work on the Drupal 11 backend that powers this site's headless CMS architecture.\n\n## What Happened on Main\n\nThe \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fdrupal.madsnorgaard.net\u002Fcompare\u002Fc492b587ece9...4a2a6333ed97\">push to main\u003C\u002Fa> moved the HEAD from c492b587ece9 to 4a2a6333ed97. This is incremental work - likely configuration adjustments, module updates, or refinements to how content types are structured for the JSON:API layer that feeds the Nuxt 3 frontend. When you run a headless setup like this one, the contract between backend and frontend matters. Small changes to the API shape can cascade if you're not careful about keeping both sides synchronized.\n\n## Security Updates Branch\n\nThe second push went to a branch called chore\u002Fsecurity-updates-jun2026, moving from \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fdrupal.madsnorgaard.net\u002Fcompare\u002F11cc4a6fddca...eaa907df6ec6\">11cc4a6fddca to eaa907df6ec6\u003C\u002Fa>. The branch name tells you what it is - security patches scheduled for June 2026. This is forward-looking maintenance. Drupal security updates follow a predictable cadence, and prepping a branch for future patches keeps the main branch stable while you test changes in isolation. You merge when you're confident the updates won't break the API contract or introduce regressions in how content entities are exposed to the frontend.\n\n## Why This Matters for a Headless Architecture\n\nThese are not feature releases. They are the kind of work that keeps a Drupal site running cleanly as a backend API. When you use Drupal 11 as a headless CMS, you are managing two surfaces - the content model inside Drupal and the data shape your frontend consumes. Every module update, every security patch, every configuration change has the potential to shift that boundary. You test on a branch. You verify the JSON:API responses still match what your Nuxt components expect. Then you merge.\n\nThe work is technical and does not announce itself, but it is what keeps a system like this stable over time. You push to main when you know it's safe. You push to a feature branch when you need to validate first.","two-pushes-drupalmadsnorgaardnet-main-and-security-updates-branch","2026-06-21T01:57:16+00:00",[],{"id":27,"title":28,"teaser":29,"body":30,"slug":31,"date":32,"tags":33},"31f7dd48-488f-4100-8c25-4df0bb7649cd","Six Consecutive Pushes to madsnorgaard.net","A rapid sequence of six incremental commits to madsnorgaard.net, detailing backend infrastructure work on a headless Drupal CMS with Nuxt frontend integration.","I pushed six commits to the main branch of madsnorgaard.net in rapid succession - a sequence of incremental updates that represent backend work on the site's infrastructure and content handling layer.\n\n\u003Ch2>The Push Sequence\u003C\u002Fh2>\n\n\u003Cp>The commits moved HEAD through six stages: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fmadsnorgaard.net\u002Fcompare\u002Faa47ec29beb9...d932852f4ca1\">aa47ec29 to d932852f\u003C\u002Fa>, then \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fmadsnorgaard.net\u002Fcompare\u002Fd932852f4ca1...8278d2b9fa70\">d932852f to 8278d2b9\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fmadsnorgaard.net\u002Fcompare\u002F8278d2b9fa70...7cde696684f0\">8278d2b9 to 7cde6966\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fmadsnorgaard.net\u002Fcompare\u002F7cde696684f0...9888c70e0469\">7cde6966 to 9888c70e\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fmadsnorgaard.net\u002Fcompare\u002F9888c70e0469...db437c092a6d\">9888c70e to db437c09\u003C\u002Fa>, and finally \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fmadsnorgaard.net\u002Fcompare\u002Fdb437c092a6d...4df35ca17dd1\">db437c09 to 4df35ca1\u003C\u002Fa>.\u003C\u002Fp>\n\n\u003Cp>This is not feature development. These are small adjustments - configuration changes, module updates, refinements to how content is structured or served. When you run a headless CMS setup where Drupal feeds a Nuxt frontend, work like this happens in clusters. You make a change, test it, find an edge case, adjust again. The commits stack up quickly.\u003C\u002Fp>\n\n\u003Ch2>What Drives This Pattern\u003C\u002Fh2>\n\n\u003Cp>The rapid-fire nature of these pushes reflects how I work on infrastructure. I do not batch changes into large releases when I am refining the backend. Each commit represents a discrete unit of work - a fix, a test, a verification step. Push early, push often. The version control system is there to track the sequence, not to wait for a polished narrative.\u003C\u002Fp>\n\n\u003Cp>This approach has trade-offs. The commit history gets noisy. But the alternative - holding changes locally while you perfect them - introduces risk. If something breaks, you want to know exactly which change caused it. Granular commits give you that precision.\u003C\u002Fp>\n\n\u003Ch2>The Nature of the Changes\u003C\u002Fh2>\n\n\u003Cp>Without access to the specific diffs, I can infer what these pushes likely touched. Content type adjustments in Drupal. Updates to how fields are exposed through the JSON:API layer. Tweaks to routing logic in the Nuxt frontend to match changes in the API shape. Small fixes to template rendering or data transformation.\u003C\u002Fp>\n\n\u003Cp>This is the kind of work that keeps a headless setup running cleanly. The contract between backend and frontend is fragile. If the API changes without corresponding updates to the consumer, things break. If field names shift or entity structures evolve, the frontend needs to know. These six pushes represent that ongoing negotiation between the two layers.\u003C\u002Fp>\n\n\u003Ch2>Why Document This\u003C\u002Fh2>\n\n\u003Cp>There is value in recording even mundane technical work. Not every commit deserves a detailed write-up, but patterns do. Six pushes in sequence say something about the current state of the project - active development, iterative refinement, attention to the infrastructure layer rather than public-facing features.\u003C\u002Fp>\n\n\u003Cp>It also serves as a marker. When I look back at the commit history months from now, I will see this cluster and remember what I was working on. The specific changes matter less than the fact that I was deep in the backend, tightening things up, making sure the foundation was solid before building on top of it.\u003C\u002Fp>\n\n\u003Cp>This is maintenance work. It does not announce itself. But it is the work that makes everything else possible.\u003C\u002Fp>","six-consecutive-pushes-madsnorgaardnet","2026-06-20T22:57:11+00:00",[],0,1,3,[38,72,113,138,179,204],{"id":39,"title":40,"tagline":41,"description":42,"coverImage":43,"liveUrl":47,"technologies":48,"status":69,"category":70,"featured":71,"sortOrder":34},"bc7d3be2-4aef-499e-ad78-3b109aaf8a5a","Skoven i Skolen","Drupal platform for outdoor learning in Danish schools","\u003Cp>Educational platform providing Danish teachers with nature-based activities and learning resources. Features an interactive year wheel with seasonal content. Drupal CMS built for performance and visual appeal. Built at Novicell.\u003C\u002Fp>",{"id":44,"url":45,"alt":46},"40bd521b-53a7-4e7d-bf22-75c865c89cc5","https:\u002F\u002Fdrupal.madsnorgaard.net\u002Fsites\u002Fdefault\u002Ffiles\u002F2026-04\u002FScreenshot%202026-04-02%20235204.png","","https:\u002F\u002Fskoven-i-skolen.dk\u002F",[49,53,57,61,65],{"id":50,"name":51,"slug":52},"338a48c6-f2de-4292-b22e-0ea7b54039df","Drupal","drupal",{"id":54,"name":55,"slug":56},"9b742b0a-8904-40c9-a993-f25c564c1c0e","PHP","php",{"id":58,"name":59,"slug":60},"4dc8fb29-745b-4557-9328-518d8f026309","Relewise","relewise",{"id":62,"name":63,"slug":64},"92722319-47f1-4ab8-8b6c-397dd479357e","Dataforsyningen API","dataforsyningen-api",{"id":66,"name":67,"slug":68},"37e66641-e02a-415c-ad9b-1d40a93101ff","WCAG AA","wcag-aa","active","professional",true,{"id":73,"title":74,"tagline":75,"description":76,"coverImage":77,"githubUrl":80,"liveUrl":81,"technologies":82,"status":69,"category":112,"featured":71,"sortOrder":34},"d19ce136-1f5e-4cdd-b608-a4dd2fcc0d71","OS2udoglær","Headless Drupal SaaS platform for Danish municipal education","\u003Cp>Headless Drupal Premium SaaS platform for OS2 - Offentligt Digitaliseringsfællesskab. One backend, multiple frontends via Transform API and Nuxt.js. Scalable multi-tenant architecture onboarding new municipalities in hours. Full open source codebase including all custom modules. Built at Novicell.\u003C\u002Fp>",{"id":78,"url":79,"alt":46},"4e2752c1-ef39-4d1c-a673-fd54acd8f58f","https:\u002F\u002Fdrupal.madsnorgaard.net\u002Fsites\u002Fdefault\u002Ffiles\u002F2026-04\u002FScreenshot%202026-04-03%20124011.png","https:\u002F\u002Fgithub.com\u002Fos2ulf\u002Fos2udoglaer","https:\u002F\u002Fulfiaarhus.dk\u002F",[83,87,88,92,96,100,104,108],{"id":84,"name":85,"slug":86},"edb0cc44-473c-4ab3-aa13-140474e8dbf0","Drupal 10","drupal-10",{"id":54,"name":55,"slug":56},{"id":89,"name":90,"slug":91},"b4f3029c-629b-41d0-a80d-bc0d87f313bd","Nuxt.js","nuxt-js",{"id":93,"name":94,"slug":95},"3f488318-a9f3-46b2-9bb5-917ed3459be9","Transform API","transform-api",{"id":97,"name":98,"slug":99},"7ddc9922-e7a9-4b7c-b18f-15a353563a5e","Solr","solr",{"id":101,"name":102,"slug":103},"103691c7-b607-4b09-90a9-9a99bf86f34c","Redis","redis",{"id":105,"name":106,"slug":107},"9fdad477-33a7-4a4a-9361-d207918104b4","Platform.sh","platform-sh",{"id":109,"name":110,"slug":111},"3a91f1ef-31a9-4887-b341-e4320dfde279","DDEV","ddev","open-source",{"id":114,"title":115,"tagline":116,"description":117,"coverImage":118,"liveUrl":121,"technologies":122,"status":69,"category":70,"featured":71,"sortOrder":34},"d5fc1654-3387-4b77-8723-591ffb7386aa","Nordisk Film","Headless Drupal multisite for Nordic entertainment leader","\u003Cp>Headless Drupal 10 corporate platform for Nordisk Film, part of the Egmont Group multisite ecosystem. Pixel-perfect design execution across film, gaming, cinema, and distribution. Reusable backend modules with interchangeable frontend per brand. Built at Novicell.\u003C\u002Fp>",{"id":119,"url":120,"alt":46},"01ddc59d-e50e-4ee9-a0fd-193e46c17deb","https:\u002F\u002Fdrupal.madsnorgaard.net\u002Fsites\u002Fdefault\u002Ffiles\u002F2026-04\u002Fnordisk-film.jpg","https:\u002F\u002Fnordiskfilm.com\u002F",[123,124,125,129,133,137],{"id":84,"name":85,"slug":86},{"id":54,"name":55,"slug":56},{"id":126,"name":127,"slug":128},"252185e0-32bb-4b16-ad0f-bbb6d817b958","Headless CMS","headless-cms",{"id":130,"name":131,"slug":132},"2244893d-db69-4fd0-b9d9-61813bf62566","Multisite","multisite",{"id":134,"name":135,"slug":136},"1f117bd1-2e28-41bb-add7-23c72cdf7ca9","Azure AD","azure-ad",{"id":105,"name":106,"slug":107},{"id":139,"title":140,"tagline":141,"description":142,"coverImage":143,"githubUrl":146,"liveUrl":147,"technologies":148,"status":69,"category":112,"featured":71,"sortOrder":35},"18f4a2be-4744-4f56-b16a-8f2874da5fff","ÅbenForms","Headless Drupal 11 + Nuxt 3 for Danish municipalities","A civic tech platform built headless: Drupal 11 as content API, Nuxt 3 as frontend. Used by Danish municipalities for public-facing forms and service delivery.",{"id":144,"url":145,"alt":46},"3b3ab627-4067-4ec9-94a1-e53602189aab","https:\u002F\u002Fdrupal.madsnorgaard.net\u002Fsites\u002Fdefault\u002Ffiles\u002F2026-04\u002FScreenshot%202026-04-03%20200612.png","https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Faabenforms","https:\u002F\u002Faabenforms.dk",[149,153,157,158,162,166,170,174,178],{"id":150,"name":151,"slug":152},"124052d0-ac24-4433-a8de-2c10cf63fb8a","Drupal 11","drupal-11",{"id":154,"name":155,"slug":156},"312eda2f-a881-46c5-981c-c1369bbdfb0f","Nuxt 3","nuxt-3",{"id":54,"name":55,"slug":56},{"id":159,"name":160,"slug":161},"8a18ca13-67d8-4169-a2ff-dd20a0f884c9","JSON:API","json-api",{"id":163,"name":164,"slug":165},"4d697a18-0ae3-4b91-8202-fa923149cc5b","ECA","eca",{"id":167,"name":168,"slug":169},"12bad585-69e0-49da-be08-65f4d3daba93","MitID","mitid",{"id":171,"name":172,"slug":173},"afad30d4-63fa-4127-90d5-388bbbeaa3e4","CPR\u002FCVR","cpr-cvr",{"id":175,"name":176,"slug":177},"cd393b4d-3f11-4044-9936-bdb19942b3a8","Docker","docker",{"id":109,"name":110,"slug":111},{"id":180,"title":181,"tagline":182,"description":183,"coverImage":184,"githubUrl":187,"liveUrl":188,"technologies":189,"status":69,"category":112,"featured":71,"sortOrder":203},"bed18850-fa22-4f8b-ac9b-c2d2b99df79c","South African History Online","15-year commitment to open historical knowledge","Technical lead since 2010. Drupal platform management, Solr search infrastructure, AI-assisted metadata system for 14,000+ history entries.",{"id":185,"url":186,"alt":46},"b41a3505-b077-4d98-b3eb-f271b5a29e5e","https:\u002F\u002Fdrupal.madsnorgaard.net\u002Fsites\u002Fdefault\u002Ffiles\u002F2026-04\u002FScreenshot%202026-04-03%20012752.png","https:\u002F\u002Fgithub.com\u002FSouth-African-History-Online\u002Fsahistory-web","https:\u002F\u002Fwww.sahistory.org.za",[190,191,192,193,194,195,199],{"id":150,"name":151,"slug":152},{"id":54,"name":55,"slug":56},{"id":97,"name":98,"slug":99},{"id":109,"name":110,"slug":111},{"id":175,"name":176,"slug":177},{"id":196,"name":197,"slug":198},"3a988625-f148-4dfa-be00-e085339ad910","Claude AI","claude-ai",{"id":200,"name":201,"slug":202},"c2de0042-deb9-47f2-9e7a-25db05de3dc9","Python","python",2,{"id":205,"title":206,"tagline":207,"description":208,"coverImage":209,"githubUrl":212,"liveUrl":213,"technologies":214,"status":69,"category":235,"featured":71,"sortOrder":36},"f616ee77-60e7-4434-b2c8-fae5215d013f","madsnorgaard.net","This site - Drupal 11 and Wordpress backend with a Nuxt 3 frontend","Portfolio and writing platform. Drupal 11 headless CMS via JSON:API, Nuxt 3 SSR frontend. Self-hosted on Contabo VPS with Docker, Traefik, and GitHub Actions CI\u002FCD.",{"id":210,"url":211,"alt":46},"80947f4a-df74-4610-92c6-ea836565110f","https:\u002F\u002Fdrupal.madsnorgaard.net\u002Fsites\u002Fdefault\u002Ffiles\u002F2026-04\u002FScreenshot%202026-04-03%20201051.png","https:\u002F\u002Fgithub.com\u002Fmadsnorgaard\u002Fmadsnorgaard.net","https:\u002F\u002Fmadsnorgaard.net",[215,216,217,221,222,223,227,231],{"id":154,"name":155,"slug":156},{"id":150,"name":151,"slug":152},{"id":218,"name":219,"slug":220},"cadbd05a-f572-4ea9-8e1c-ba12505e2f65","WordPress","wordpress",{"id":159,"name":160,"slug":161},{"id":175,"name":176,"slug":177},{"id":224,"name":225,"slug":226},"33ae4b93-d82c-4277-ae40-d5427ea41dd5","Traefik","traefik",{"id":228,"name":229,"slug":230},"6f264577-fa19-4aff-a051-16c9a4dfea03","GitHub Actions","github-actions",{"id":232,"name":233,"slug":234},"0725c10e-1212-47d7-9045-cfbe08bd0973","Contabo VPS","contabo-vps","personal",{"lastCommit":237,"activeLanguages":240,"lastPhoto":243,"availability":7,"location":6,"employer":246},{"message":46,"repo":238,"timeAgo":239},"bathong","5 days ago",[241,242,55],"Vue","TypeScript",{"title":244,"archiveNumber":46,"url":245},"UCT council votes to remove the Rhodes statue. University of Cape Town, South Africa, April 2015.","https:\u002F\u002Fphoto.madsnorgaard.net\u002Fuct-council-votes-to-remove-the-rhodes-statue-university-of-cape-town-south-africa-april-2015\u002F","Eksponent",[248,272,293,311,328,346,368,390],{"id":249,"title":244,"caption":250,"date":251,"slug":252,"url":245,"image":253},1313,"On 8 April 2015, UCT's council met to decide the fate of the statue of Cecil John Rhodes that had stood on the university's upper campus since 1934. The meeting was disrupted by students who had been occupying the space around the statue for weeks as part of the Rhodes Must Fall movement. The council voted to remove the statue the following day. The campaign marked a turning point in a broader public debate across South Africa about colonial symbols in shared civic and academic spaces.","2026-03-27T21:35:34","uct-council-votes-to-remove-the-rhodes-statue-university-of-cape-town-south-africa-april-2015",{"src":254,"alt":255,"width":256,"height":257,"variants":258},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F03\u002Frhodes-must-fall-2015-0006-1024x683.jpg","On April 8, at the end of a meeting disrupted by students, UCT’s council ruled that the statue would be removed the following day.",1200,800,[259,263,267,270],{"url":260,"width":261,"height":262},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F03\u002Frhodes-must-fall-2015-0006-300x200.jpg",300,200,{"url":264,"width":265,"height":266},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F03\u002Frhodes-must-fall-2015-0006-768x512.jpg",768,512,{"url":254,"width":268,"height":269},1024,683,{"url":271,"width":256,"height":257},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F03\u002Frhodes-must-fall-2015-0006.jpg",{"id":273,"title":274,"caption":274,"date":275,"slug":276,"url":277,"image":278},1312,"Domestic worker walking past window in Capricorn Park, Cape Town, 2009.","2026-03-27T21:27:07","domestic-worker-walking-past-window-in-capricorn-park-cape-town-2009","https:\u002F\u002Fphoto.madsnorgaard.net\u002Fdomestic-worker-walking-past-window-in-capricorn-park-cape-town-2009\u002F",{"src":279,"alt":274,"width":280,"height":281,"variants":282},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0017-1024x662.jpg",1500,970,[283,286,289,291],{"url":284,"width":261,"height":285},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0017-300x194.jpg",194,{"url":287,"width":265,"height":288},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0017-768x497.jpg",497,{"url":279,"width":268,"height":290},662,{"url":292,"width":280,"height":281},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0017.jpg",{"id":294,"title":295,"caption":296,"date":297,"slug":298,"url":299,"image":300},1318,"Men playing a game of Dominos in the street, Woodstock 2014.","Men gather around a worn table in the street to play dominoes, their hands moving through dappled shade. Woodstock, Cape Town, South Africa, 2014.","2026-03-27T20:14:14","men-playing-a-game-of-dominos-in-the-street-woodstock-2014","https:\u002F\u002Fphoto.madsnorgaard.net\u002Fmen-playing-a-game-of-dominos-in-the-street-woodstock-2014\u002F",{"src":301,"alt":295,"width":280,"height":302,"variants":303},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0001-1024x683.jpg",1000,[304,306,308,309],{"url":305,"width":261,"height":262},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0001-300x200.jpg",{"url":307,"width":265,"height":266},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0001-768x512.jpg",{"url":301,"width":268,"height":269},{"url":310,"width":280,"height":302},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0001.jpg",{"id":312,"title":313,"caption":314,"date":315,"slug":316,"url":317,"image":318},1311,"Wedding in Johannesburg, South Africa","Guests depart carrying gifts on a wet street outside a family home. Johannesburg, South Africa, 2017.","2026-03-27T19:59:42","johannesburg-south-africa","https:\u002F\u002Fphoto.madsnorgaard.net\u002Fjohannesburg-south-africa\u002F",{"src":319,"alt":314,"width":280,"height":302,"variants":320},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FJohannesburg-family-mads-norgaard-0007-1024x683.jpg",[321,323,325,326],{"url":322,"width":261,"height":262},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FJohannesburg-family-mads-norgaard-0007-300x200.jpg",{"url":324,"width":265,"height":266},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FJohannesburg-family-mads-norgaard-0007-768x512.jpg",{"url":319,"width":268,"height":269},{"url":327,"width":280,"height":302},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FJohannesburg-family-mads-norgaard-0007.jpg",{"id":329,"title":330,"caption":331,"date":332,"slug":333,"url":334,"image":335},1307,"Overlooking Blikkiesdorp, 2010","Overlooking one of Western Cape's temporary housing projects, Blikkiesdorp - Afrikaans for \"Tin Can Town\" - built ahead of the 2010 FIFA World Cup to relocate informal settlement residents from areas designated for tourist development. What was promised as temporary became permanent for thousands of families. Photographed from the perimeter, 2010.","2026-03-26T09:48:17","overlooking-one-of-western-capes-temporary-housing-projects-blikkiesdorp-2010","https:\u002F\u002Fphoto.madsnorgaard.net\u002Foverlooking-one-of-western-capes-temporary-housing-projects-blikkiesdorp-2010\u002F",{"src":336,"alt":337,"width":280,"height":302,"variants":338},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0008-1024x683.jpg","Overlooking one of Western Cape’s temporary housing projects, Blikkiesdorp, 2010.",[339,341,343,344],{"url":340,"width":261,"height":262},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0008-300x200.jpg",{"url":342,"width":265,"height":266},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0008-768x512.jpg",{"url":336,"width":268,"height":269},{"url":345,"width":280,"height":302},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FExhibition-1-all-final-0008.jpg",{"id":347,"title":348,"caption":349,"date":350,"slug":351,"url":352,"image":353},1290,"Rugby at Newlands watching the Stormers","Newlands Stadium, Cape Town. Stormers match, year I have forgotten but the photograph remembers it.\n\n\n\nI was in the crowd, not behind it. That choice is the photograph. The try line is in the background, the arms are in front, and somewhere in between is the reason I brought a camera instead of just watching.\n\n\n\nI left Cape Town in 2018.","2026-03-25T09:35:02","rugby-at-newlands-watching-the-stormers","https:\u002F\u002Fphoto.madsnorgaard.net\u002Frugby-at-newlands-watching-the-stormers\u002F",{"src":354,"alt":348,"width":355,"height":356,"variants":357},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2026\u002F03\u002FRugby-at-Newlands-watching-the-Stormers-1-1024x683.jpg",2048,1365,[358,360,362,363,366],{"url":359,"width":261,"height":262},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2026\u002F03\u002FRugby-at-Newlands-watching-the-Stormers-1-300x200.jpg",{"url":361,"width":265,"height":266},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2026\u002F03\u002FRugby-at-Newlands-watching-the-Stormers-1-768x512.jpg",{"url":354,"width":268,"height":269},{"url":364,"width":365,"height":268},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2026\u002F03\u002FRugby-at-Newlands-watching-the-Stormers-1-1536x1024.jpg",1536,{"url":367,"width":355,"height":356},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2026\u002F03\u002FRugby-at-Newlands-watching-the-Stormers-1.jpg",{"id":369,"title":370,"caption":371,"date":372,"slug":373,"url":374,"image":375},1160,"COSATU march in Cape Town","Approximately 10.000 people gathered in Keizersgracht Street on the 6th of August for a march to Parliament. This was done to protest against the rising electricity, fuel and food prices. The protesters expressed their lack of faith in the government's ability to fix the country's economic problems. Having failed to meet the peoples needs for so long, the politicians are the main cause of the marchers activism.","2018-07-05T13:02:41","cosatu-march-in-cape-town","https:\u002F\u002Fphoto.madsnorgaard.net\u002Fcosatu-march-in-cape-town\u002F",{"src":376,"alt":377,"width":280,"height":378,"variants":379},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0027-1-1024x685.jpg","Copyright © Mads Norgaard - madsnorgaard.net\u002Fcopyright",1004,[380,383,386,388],{"url":381,"width":261,"height":382},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0027-1-300x201.jpg",201,{"url":384,"width":265,"height":385},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0027-1-768x514.jpg",514,{"url":376,"width":268,"height":387},685,{"url":389,"width":280,"height":378},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F07\u002FWebsite-additional-mads-norgaard-0027-1.jpg",{"id":391,"title":392,"caption":393,"date":394,"slug":395,"url":396,"image":397},862,"The evening train to Khayelitsha","It is 17:35 and one of the trains to Khayelitsha departs from Cape Town's train station. The masses of people travelling on this line are packed into the train coaches. The carriages get so full that people hang out of the windows and doors or stand in between the coaches. This of course is a dangerous activity but cannot be helped as there is simply not enough space during the daily rush hours. After an already long day at work in town the commuters have to endure a last travel to their homes. The environment is untidy, dark and there is no space to move around. It gets hot in the train, making it almost impossible to breathe, yet the mood is light and people enjoy conversation, singing and perhaps a packet of Nik Naks or Spookies.","2018-06-19T09:44:43","evening-train-to-khayelitsha","https:\u002F\u002Fphoto.madsnorgaard.net\u002Fevening-train-to-khayelitsha\u002F",{"src":398,"alt":392,"width":399,"height":400,"variants":401},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F06\u002Ftrain-to-khayelitsha-0001-670x1024.jpg",1309,2000,[402,405,407,410,413],{"url":403,"width":404,"height":261},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F06\u002Ftrain-to-khayelitsha-0001-196x300.jpg",196,{"url":398,"width":406,"height":268},670,{"url":408,"width":265,"height":409},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F06\u002Ftrain-to-khayelitsha-0001-768x1173.jpg",1173,{"url":411,"width":412,"height":365},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F06\u002Ftrain-to-khayelitsha-0001-1005x1536.jpg",1005,{"url":414,"width":399,"height":400},"https:\u002F\u002Fphoto.madsnorgaard.net\u002Fwp-content\u002Fuploads\u002F2018\u002F06\u002Ftrain-to-khayelitsha-0001.jpg"]