# Murat Çorlu > Web Development, Ghost and running Synaps Media Public Ghost content for AI and LLM tooling. This file includes a bounded export of public pages first, then recent public posts. Append `.md` to any post or page URL to get the content in Markdown (for example, `/example-post.md`). ## Pages ### Murat Çorlu URL: https://muratcorlu.com/about/ Last updated: 2025-12-10T12:03:37.000Z I was born and raised in Sakarya, a city near Istanbul, Türkiye. My journey with computers began at the age of thirteen, in 1995, when I first encountered one at my cousin’s house. It was love at first sight. After my persistent requests, my father bought me a second-hand Commodore Amiga 500, as it was affordable. Initially, I spent months playing games, fascinated by how everything worked, until I met an older friend who owned a Commodore 64\. He was doing something magical: coding. I asked, “Please teach me how to write that,” referring to the hangman game code he showed me. This moment introduced me to QBasic. For two years, I tinkered with QBasic, experimenting and learning. Later, my father bought me a PC, a Pentium 166\. At a bookstore, I discovered a thick book titled “Visual Basic.” The word “Basic” in the title intrigued me, and I started reading it, realizing I could learn this too. I developed many Windows applications for personal use, like a batch file renamer (to organize my MP3s), a birthday reminder, and a digital version of my high school graduation yearbook in 2000. In 1998, internet cafes were popular in my town, and one was owned by someone I knew. During a visit, I noticed he was manually tracking customer usage times and charges, using stickers and calculations. I suggested, “An application would help you manage this,” to which he replied, “If you make one, it would definitely help.” I spent the next two nights coding and created a basic application for him. He was thrilled and immediately adopted it, rewarding me with free access to the café. This was the first time I realized coding could help people and even bring in some earnings. With the internet’s rise, I expanded my programming knowledge, eventually shifting to web development. Excited by the idea of creating something that could reach anyone worldwide, I learned ASP, then ASP.NET, PHP, and Linux. I continued to make digital yearbooks (using Adobe Director) until 2006\. After university, I started my professional programming career at small companies, progressing step by step to roles in Türkiye’s top internet firms. Now, in my 40s, I’m still learning daily and constantly exploring new projects and ideas. I’m passionate about creating useful things through programming, the best tool I have. So, I’m Murat Çorlu, a software developer based in The Netherlands. This blog is about my work. I also write a [Turkish blog](https://muratcorlu.net/?ref=muratcorlu.com) and micro-blog on [Fediverse](https://muratcorlu.com/notes/) and [Bluesky](https://bsky.app/profile/muratcorlu.com?ref=muratcorlu.com). ### Blogroll URL: https://muratcorlu.com/blogroll/ Last updated: 2026-05-08T17:50:10.000Z Here a list of blogs that I follow and find very helpful about web development. Show all recommendations [A Beautiful SiteA blog about everything web. Est. 2007![](https://www.abeautifulsite.net/images/logos/leaf.png)![](http://0.gravatar.com/avatar/bf1b3b95fd5b096a3592247c29667b33?s=512)](https://www.abeautifulsite.net/?ref=muratcorlu.com) Creator of Shoelace. Articles about Design Systems and Web Components [Adactio: Jeremy KeithPublications and presentations from web developer and author, Jeremy Keith.![](https://adactio.com/icon.png)adactioJeremy Keith![](https://adactio.com/images/photo-300.jpg)](https://adactio.com/articles?ref=muratcorlu.com) [Brad FrostDesign Systems Consulting, Web Development, Workshops, and Music![](https://bradfrost.com/favicon.ico)Brad Frost![](https://bradfrost.com/wp-content/uploads/2017/08/card-image.png)](https://bradfrost.com/blog/?ref=muratcorlu.com) [Home - Ahmad Shadeed![](https://ishadeed.com/assets/favicon-32x32.png)Ahmad Shadeed BlogiShadeed![](http://ishadeed.com/assets/post-fb.jpg)](https://ishadeed.com/?ref=muratcorlu.com) Very good, detailed articles about CSS [Chen Hui JingThe chronicles of a self-taught designer and developer.![](https://chenhuijing.com/assets/favicons/apple-touch-icon.png)HomeChen Hui JingThe chronicles of a self-taught designer and developer.![](https://chenhuijing.com/assets/images/avatar-mask.png)](https://chenhuijing.com/?ref=muratcorlu.com#%F0%9F%96%8C) Another CSS focused great blog [Blog– The personal website of Sara Soueidan, inclusive design engineer![](https://cdn.synaps.media/muratcorlucom/content/images/icon/icon.svg)![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/og.png)](https://www.sarasoueidan.com/blog/?ref=muratcorlu.com) [Josh W ComeauFriendly tutorials for developers. Focus on React, CSS, Animation, and more!![](https://www.joshwcomeau.com/assets/favicon.png?v=4)![](https://www.joshwcomeau.com/images/og-default.png)](https://www.joshwcomeau.com/?ref=muratcorlu.com) [Max BöckMax Böck is a professional front-end developer based in Vienna, Austria.![](https://mxb.dev/assets/images/favicon/apple-touch-icon.png?v=1)Max BöckMax Böck![](https://mxb.dev/assets/images/avatar2.jpg)](https://mxb.dev/?ref=muratcorlu.com) A front-end focused blog that is pure art [Aleksandr HovhannisyanDev tutorials, thoughts on software development, and the occasional off-topic post.![](https://www.aleksandrhovhannisyan.com/assets/images/favicons/favicon-228.png)Aleksandr HovhannisyanAleksandr Hovhannisyan![](https://www.aleksandrhovhannisyan.com/assets/images/ePJrTaUxUe-400.jpeg)](https://www.aleksandrhovhannisyan.com/?ref=muratcorlu.com) [Kilian ValkhofFront-end & user experience developer![](https://kilianvalkhof.com/wp-content/themes/kvsixteen/static/img/favicon.png)Kilian Valkhof | Front-end & user experience developer![](https://kilianvalkhof.com/wp-content/uploads/og.jpg)](https://kilianvalkhof.com/?ref=muratcorlu.com) ### Tools URL: https://muratcorlu.com/tools/ Last updated: 2025-09-26T21:55:45.000Z I like tiny tools and services, those make one job and do it perfectly. I wanted to take list of them on my site. Most of them are free tools. Some links contain my referral codes. I'll update this post in time. ## Design [SquooshSquoosh is the ultimate image optimizer that allows you to compress and compare images with different codecs in your browser.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/icon-large-maskable-c2078ced-1.png)Squoosh![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/icon-large-maskable-c2078ced-1.png)](https://squoosh.app/?ref=muratcorlu.com) A great offline web app to optimize images easily [SVG Icon Libraries and Custom Icon Font Organizer ❍ IcoMoonEasily mange your icons and integrate them in your projects. Browse free icons or import your own SVG icons to export as icon font, SVG, PNG, sprite and more.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-touch-icon-35f4a648bf9ba38347b4ab81eb7a06ba-1.png)](https://icomoon.io/?ref=muratcorlu.com) Great online tool to create custom font file with SVG files [Excalidraw — Collaborative whiteboarding made easyExcalidraw is a virtual collaborative whiteboard tool that lets you easily sketch diagrams that have a hand-drawn feel to them.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-touch-icon-4.png)Excalidraw![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/og-image-3.png)](https://excalidraw.com/?ref=muratcorlu.com) Simple yet powerful, free web based whiteboard tool ## Monitoring [Pushover: Simple Notifications for Android, iPhone, iPad, and Desktop![](https://cdn.synaps.media/muratcorlucom/content/images/icon/favicon.ico)![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/writing-header-c251fe65a610047a0209640b75d38e79f7b23b67d64135f629e6062f3e25c2f3.png)](https://pushover.net/?ref=muratcorlu.com) I Pushover for years for many of my projects. Great service [Beszel | Simple, lightweight server monitoringLightweight server monitoring with historical data, Docker stats, and alerts.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/icon-1.svg)Beszel![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/social.png)](https://beszel.dev/?ref=muratcorlu.com) It's easy to use and quite powerful ## Network [HTTPie CLI: HTTP & API testing clientJSON, syntax highlighting, sessions, wget-like downloads, plugins, and more.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-icon-180x180.png)HTTPie CLI: HTTP & API testing client![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/cli.png)](https://httpie.io/cli?ref=muratcorlu.com) It's way more intuitive then curl. Great for testing HTTP endpoints from terminal. You can combine with [jq](https://jqlang.org/?ref=muratcorlu.com) [DNS Propagation Checker - Global DNS Checker ToolInstant DNS Propagation Check. Global DNS Propagation Checker - Check DNS records around the world.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-touch-icon-2.png)Global DNS Testing Tool![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/logo-og.png)](https://www.whatsmydns.net/?ref=muratcorlu.com) For testing to check if a DNS change updated worldwide [Measure TTFB from 40 Locations - SpeedVitalsPerform TTFB (Time to First Byte) Test on your Website from 40 Locations Worldwide and find out how fast is your website.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-touch-icon-3.png)SpeedVitals![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/speedvitals_banner.png)](https://speedvitals.com/ttfb-test?ref=muratcorlu.com) TTFB is a good indicator about your site's performance. This tool allows you to test it worldwide [intoDNS: checks DNS and mail servers healthintoDNS checks the health and configuration of DNS and mail servers.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/favicon-1.ico)Elvsoft![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/intodns_logo.gif)](https://intodns.com/?ref=muratcorlu.com) It's an old service but still working well. Good for checking your DNS server setup [IPRoyal | Premium Quality Proxies, Unbeatable PricesGet a proxy service provider without contracts, non-expiring traffic, and easy self-service.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/favicon.svg)IPRoyal.com![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/IPR_Web_Preview_2845a84108.jpg)](https://iproyal.com/?r=918853&ref=muratcorlu.com) Great tool to check your endpoints with the requests from -nearly- every countries [Newsletters spam test by mail-tester.commail-tester.com is a free online service that allows you to test your emails for Spam, Malformed Content and Mail Server Configuration problems![](https://cdn.synaps.media/muratcorlucom/content/images/icon/favicon.png)![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/logo-273x273.png)](https://www.mail-tester.com/?ref=muratcorlu.com) Test your mail settings to prevent going spam folder [badssl.com![](https://cdn.synaps.media/muratcorlucom/content/images/icon/icon-blue.png)![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/chrome.svg)](https://badssl.com/?ref=muratcorlu.com) A list of HTTP endpoints to test your HTTP client against all type of SSL issues ## Data Storage [PocketBase - Open Source backend in 1 fileOpen Source backend in 1 file with realtime database, authentication, file storage and admin dashboard![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-touch-icon-1.png)PocketBase v0.28.4![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/og-1.png)](https://pocketbase.io/?ref=muratcorlu.com) A simple and very flexible database. Can be great fit for panel-like projects ## Development Tools [Webhook.site - Test, transform and automate Web requests and emailsInstantly generate a free, unique URL and email address to test, inspect, and automate (with a visual workflow editor and scripts) incoming HTTP requests and emails![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-touch-icon.png)Webhook.site![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/icon.png)](https://webhook.site/?ref=muratcorlu.com) This free service creates random endpoints for you that you can test webhooks ### Notes URL: https://muratcorlu.com/notes/ Last updated: 2025-12-20T19:38:17.000Z To follow me on fediverse (like Mastodon) search for `@murat@muratcorlu.com` on your platform. Here is the latest notes that I posted. Want to add something like to your Ghost site? [I have an article](https://muratcorlu.com/adding-ghost-social-web-notes-to-your-website-2/) for this. ### Contact URL: https://muratcorlu.com/contact/ Last updated: 2026-01-27T23:48:00.000Z If you need to reach me, you can use this form. ## Posts ### Using inline partials in Ghost Themes URL: https://muratcorlu.com/using-inline-partials-in-ghost-themes/ Last updated: 2026-07-02T20:10:27.000Z Another thing I learned while working on Synaps Media landing page. First let me tell the need: [My designer](https://www.enes.design/?ref=muratcorlu.com) delivered me some reusable section UIs. When you have repeating components, it's a well known practice to use parametric Handlebars partials, and reuse them in multiple places with different values. Testimonials bubbles in our landing page is a good example. ```handlebars
{{quote}}
{{#if avatar}} {{name}} {{/if}}
{{name}}

{{handle}}

``` And in a place that I want to show a testimonial bubble, I use it like below: ```handlebars {{> "testimonial" quote="Some comments" avatar="https://...." name="John Doe" handle="john.doe" }} ``` This is nice for simple use cases. But I had a section with a slider that I'll highlight some features. And in some cases you might want to pass large HTML content for some targets. To achieve this, in our partials we need to use "[inline partials](https://handlebarsjs.com/guide/partials.html?ref=muratcorlu.com#inline-partials)". ```handlebars

{{#> heading}}Change title{{/heading}}

{{#> description}}Change description{{/description}}

``` `{{#> features}}` part, create an inline partial with a default value inside. And here how we use it: ```handlebars {{#> "section-feature-slide"}} {{#*inline "heading"}}Built for modern publishers{{/inline}} {{#*inline "description"}}Slide through the features that make Synaps Media the privacy-first analytics platform you actually enjoy using.{{/inline}} {{#*inline "features"}}
  • Real-time analytics

    Watch visits land as they happen, with zero cookies and no consent banners.

  • Invisible traffic

    Bots, RSS readers and email clients that client-side tracking always misses.

  • .... {{/inline}} {{/ "section-feature-slide"}} ``` Similar to regular partials, with `{{#> "section-feature-slide"}}` syntax, we say "I want this partial here". But this time we are able to give partial block contents inside. `{{#*inline "features"}}` says "put this content inside "features" block in our partial. That's very handy. Very similar to how "[slots](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/slot?ref=muratcorlu.com)" work on web components. Even though syntax is not very intuitive, it does the job. ### Using contentFor helper in Ghost Themes URL: https://muratcorlu.com/using-contentfor-helper-in-ghost-themes/ Last updated: 2026-07-02T18:13:56.000Z While working on new landing page of Synaps Media, I used a lot of inline CSS code. My intention was keeping section partials self-contained, like a component. So a basic section partial was something like: ```handlebars
    ...
    ``` This worked well. At the end I had the only styles that is really needed inside the document. With [Smart Optimization](https://www.synapsmedia.com/changelog/introducing-smart-optimization/?ref=muratcorlu.com) built-in to Synaps Media, these styles were also minified automatically, so not worried about waste of resource. I know ideally it's better to keep styles in external CSS files, but because of the network latency, I didn't want to fined by [Page Speed](https://pagespeed.web.dev/?ref=muratcorlu.com) for this. But another side effect was having many `style` tags inside `body`. Practically this is not a big issue as well. But I saw some warnings about this in HTML validators. If you care standards, `style` tag is not allowed inside `body` even though all modern browsers render it very well. I wanted to take my chance to pass this rule. But if I move all styles to `head` (or a separate file), then I would lose that some of my current achievements: I'll serve some unneeded styles for current page and I'll lose those self-contained handlebars templates. I used to use [Django Framework](https://www.djangoproject.com/?ref=muratcorlu.com) in the past. Django templates (jinja) also uses inheritance and you were able to inherit multiple levels of templates and fill blocks on upper level templates in extended templates. Would it be possible to have the similar in Handlebars? I’ve learned that, at the point I’ve reached, this is possible with the help of [contentFor](https://docs.ghost.org/themes/helpers/utility/block?ref=muratcorlu.com). In my `default.hbs` file, I created a `head` block: ```handlebars ... {{{block "head"}}} {{{body}}} ``` And in my section partial file I used `contentFor` to place my styles in to page's head section: ```handlebars {{#contentFor "head"}} {{/contentFor}}
    ...
    ``` The good part is, `contentFor` works even if the block is not defined in the parent of current template. In my case, my partials are included in `home.hbs` file, which extends `default.hbs`. And surprisingly `contentFor` worked well. Now I still have style definitions of sections inside the partial template, but result HTML has styles in head section. All items are checked! Hope you will find this information helpful! ### 44 URL: https://muratcorlu.com/44/ Last updated: 2026-06-25T10:39:01.000Z Today I turned 44\. And I have never been this close to making a living from my own business, something I had been dreaming about and trying to build since my university years. My [Synaps Media](https://www.synapsmedia.com/?ref=muratcorlu.com) adventure, which I started two years ago, has turned into a success story thanks to 20 years of prior experience and a number of other factors that came together at the right time. Along the way, I have learned a great deal, and I keep learning new things and tackling new challenges every day. But something is different now: every obstacle I get through adds to my knowledge. Instead of the same recurring problems I faced in my previous jobs, I now have fresh and new ones. Recurring problems wear down your motivation. But a problem you are facing for the first time actually gives you a boost of confidence when you solve it. As a new entrepreneur who has reached a certain level of success, I want to write about a few things I have learned. ## Time. Just a little more time. Successful startup stories on the internet almost always tell us about big achievements reached in a very short time. "I hit $10K MRR in 3 months," "I surpassed my salary in 6 months" — stories like these give us the message that reaching a sustainable income is only possible through scenarios like these. Until I left Trendyol and started trying to build my own business, I used to say the same things. "If I just had a year without needing a salary, I would definitely be earning this much from one of my projects." I also used to say, "Any project that hasn't matched my salary within a year has already failed." I no longer think that way. Of course, sometimes it is possible to reach very high income within a year. But from what I have seen, that is the exception, not the rule. And on top of being rare, the real cost behind those stories is rarely shown. Is it realistic for you to do the following in order to reach a very high income goal in a very short time? - Working 18 hours a day for months, spending less time with family and friends, not being there when they need you - "Fake it till you make it" — putting fake reviews and made-up numbers on your product page - Using psychological tricks to turn even people who don't need your product into customers - Building meaningless or even harmful products just because they have high market potential - Finding an idea that is "easy to sell," making long-term promises to customers, and then selling the business at the first attractive offer six months later I know — these are the building blocks of today's marketing industry. Refusing to do them might seem naive. But I believe that when the goal is making money, ethics should not be treated as a minor detail. If life has a purpose, it should not be to earn as much as possible, but to create as much as possible — to create things that are useful for people. And to do that, you need time. If you are building something genuinely good, you need to give it time to reach people and earn their trust. Especially today, with so much information noise, scattered attention, heavy news cycles, financial worries, and a crisis of trust — even a great project can take a very long time to reach its potential customers. As someone who is 44 years old and has tried to build close to a hundred projects, I can say this: to know whether a project should be abandoned or continued, you need to give it at least 2 to 3 years and put in the real work during that time. This is often true even for large, well-funded projects. Between 2009 and 2015, I worked at Sahibinden.com, which was Turkey's leading classifieds site. I was surprised to learn that the site, which launched in 2000, did not become profitable until seven years later. But that determination eventually gave them hundreds of employees and a value of hundreds of millions of dollars. The founders had both money and time. If they had given up after a year of not seeing expected results — like many people do today — Turkey's biggest classifieds site would belong to someone else. ## Building a project while working a day job For people who did not inherit wealth, the biggest challenge of running your own business is simply paying the bills. Working 40 hours a week at a job and then trying to develop a product in your remaining time is genuinely exhausting. Doing this while being married and having kids, without sacrificing something, feels almost impossible. Until I left Trendyol in 2024, I had been working continuously for nearly 20 years. And for almost all of those 20 years, I had side projects running alongside my main job. There were times I even took time off work to focus on a project. But running a project this way is not sustainable. Something you could finish in two or three months working full time ends up taking a year. That drains your energy and motivation, and it delays the early feedback and small wins that would otherwise keep you going. I was able to break out of this cycle thanks to a more stable life in the Netherlands, some savings I had built up, and my wife's support. Without those things, Synaps Media would have taken years longer to reach where it is now — if it had gotten here at all. My advice to you: if you don't have enough savings to go without a salary for a while, work toward building that cushion first. Or consider working part-time, or getting support from people close to you — so you can invest in your project in a sustainable and meaningful way. One important but often overlooked factor is your spending. When I decided to keep going on a limited budget, being much more careful with my expenses gave me a significantly longer runway. Avoiding spending that wouldn't improve my work output and would only shorten how far I could go made a real, visible difference. That is worth thinking about. Of course, finding an investor is another option that comes up a lot. Honestly, when I looked into the world of startup investment, I realized it wasn't for me. With a few exceptions, when you take investor money, you and your project become just one asset in their portfolio. They are not interested in what you're building, how you're building it, or whether your customers are happy. The only thing they care about is how much their money will grow in one or two years. So when you build with investor funding, you end up in a frantic race that never stops. Even if your project earns enough to sustain you and your team, even if your customers love what you've built, if you can't deliver 2x or 3x returns to your investor next year, your project is doomed. We often see great, useful, well-loved projects go in strange directions — or shut down entirely — simply because they couldn't satisfy their investors. A successful exit can still be a great outcome for people building these projects. But if, like me, you want to earn a living while also creating something genuinely useful, an investor can become your biggest obstacle over time. ## Touching people's lives The greatest satisfaction my work has given me is reaching people all around the world through the software I build, and helping them create value in their own lives. Even when I worked at Sahibinden.com, I used to feel a real sense of pride hearing strangers on public transit talking about how great the site was. And I was just one of hundreds of people working there. At Synaps Media, that motivation is at its highest. I host more than 250 websites belonging to nearly 200 people from over 40 countries. I have had conversations with almost all of them. Through those conversations, I have encountered very different worlds and very different perspectives. But there was one thing they all had in common: human emotions. I take genuine pleasure in the fact that what I do feels less like a robotic service and more like a neighborhood shop — professional, yes, but with a personal touch. I try never to forget that I am a human being and that the person on the other side is also an individual living their own busy life. I do my best for them with that in mind. This seems to be noticed, because I now regularly read wonderful comments on social media about both the service and about me personally. Very few things make me happier than that. If you also find joy in creating useful things, then build things around that. Don't let a small market size discourage you. Of course, evaluating whether the return is worth the effort is a basic requirement for any business. But jumping into projects that have no real value for you or for anyone else — just because a lot of people are throwing money at that space right now — does not seem like a sustainable business model to me. ## What's next I don't want anyone reading this to get the impression that I have completely figured it all out. There is still a lot of ground to cover at Synaps Media. My current goal is for the project's income to reach a level by the end of the year where I no longer need to take on extra work on the side, and to be in a position to bring on a team member to share the load and reduce the risk. The way things are going suggests this is a realistic target. I will keep doing the best I can, and we will see where things go. ### Socialize like it's 70s! URL: https://muratcorlu.com/socialize-like-its-70s/ Last updated: 2026-06-04T12:33:05.000Z Today I was reading this awesome article: [The Boring InternetThe internet you grew up on isn’t dying. A commercial veneer glued on top of it is. A visual essay about what actually persists.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-icon)Terry GodierTerry Godier![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/opengraph-image.png)](https://www.terrygodier.com/the-boring-internet?ref=muratcorlu.com) When I reached to the point he talks about [finger protocol](https://en.wikipedia.org/wiki/Finger%5F%28protocol%29?ref=muratcorlu.com), I directly wanted to try if that would work with ActivityPub handles. Unfortunately it didn't work. I made a quick research and learned some details about how `finger` protocol works, differences between [WebFinger](https://en.wikipedia.org/wiki/WebFinger?ref=muratcorlu.com) which ActivityPub uses. Then started looking for options to make ActivityPub handles work with this old friend, `finger`. With the help of Claude, it didn't take long to write a finger to ActivityPub proxy server and make it live with a fancy landing page. And here it is: [finger.fedi.pub — Socialize like it’s 70sConnect to ActivityPub actors via plain TCP port 79\. No JavaScript. No tracking. Just text.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/finger-fedi-pub.jpg)](https://finger.fedi.pub/?ref=muratcorlu.com) It's a few hundreds lines of Python code listening 79 port, and responds with nice text based informations about the ActivityPub handles you query. Here an example response: ```bash ➜ ~ finger murat@muratcorlu.com@finger.fedi.pub [finger.fedi.pub] ════════════════════════════════════════════════════════════════════════ Murat Çorlu (@murat@muratcorlu.com) ──────────────────────────────────────────────────────────────────────── Type : Person Profile : https://muratcorlu.com/ ──────────────────────────────────────────────────────────────────────── Bio: Software engineer and entrepreneur based in The Netherlands. Founder of synapsmedia.com, a Managed Hosting for Ghost CMS ──────────────────────────────────────────────────────────────────────── Recent posts (5 shown): [1] 2026-04-12 07:00 UTC Article Ghost Theme Editor → https://muratcorlu.com/ghost-theme-editor/ [2] 2026-04-12 00:07 UTC Note Ah, what a night! → https://muratcorlu.com/.ghost/activitypub/note/af0f41f5-a2f9-478f-9412-b8352671434e [3] 2026-03-30 13:54 UTC Note I'm listed as one of the 10 Most Influential People Shaping the Future of Business in 2026! ...Well, if I'm ok to pay $1000 for this. :( → https://muratcorlu.com/.ghost/activitypub/note/568b9211-0835-4eb5-8e64-eba1748242b1 [4] 2026-03-20 11:28 UTC Note I'm glad to start this with ThemeX. I love Trong's works, and now Synaps Media hosts demos of those awesome themes! https://www.synapsmedia.com/changelog/launching-free-ghost- theme-demo-hosting-starting-with-themex-studio/ → https://muratcorlu.com/.ghost/activitypub/note/374196db-021f-4b7b-b8f1-7c229dc1983a [5] 2026-03-18 14:07 UTC Note Anyone would like to have a menu bar app for Ghost? I needed this to check my social web timeline and quickly post notes. Would you have other use cases? → https://muratcorlu.com/.ghost/activitypub/note/8facef98-5e18-40a4-ac37-d346c45cc8e4 ════════════════════════════════════════════════════════════════════════ ``` It's dynamically fetching requested handle from its WebFinger endpoints, parses them and generate text based responses. `finger` command is already built-in on unix based systems like Linux and macOS. If you use one of them, just open a terminal and write: ```bash finger murat@muratcorlu.com@finger.fedi.pub ``` Or any ActivityPub handles from any other Ghost sites, Mastodon accounts, Wordpress sites enabled ActivityPub. It's definitely not something you will rely on daily, but to me, it's fascinating to see we can still make useful things with the protocols created 50+ years ago. This is the great side of open protocols, over company backed systems. They last forever, they are independent, and easy to rely on. Let's don't miss this and be aware of the importance of using decentralized protocols. ### Ghost Theme Editor URL: https://muratcorlu.com/ghost-theme-editor/ Last updated: 2026-05-21T14:06:18.000Z Update - 21 may 2026 Inspired by this work, Ghost now includes a built-in theme editor starting from version 6.39\. I'm grateful to Ghost team for the quick and clean implementation. [Read official announcement ](https://ghost.org/changelog/theme-editor/?ref=muratcorlu.com) Yesterday, I was writing notes for a backlog feature for Synaps Media called “Theme Editor”. The idea was to add a theme editor to the Synaps Media Panel, so customers can edit their theme files directly without the usual “download, unzip, edit, zip, upload” flow. This is one of the most frustrating parts of setting up a Ghost blog. So much hassle just to change the label of that “Subscribe” button. Creating a pull request on Ghost for this felt too risky. It would be a huge PR and very unlikely to be merged. Since I now host hundreds of Ghost sites on Synaps Media, I thought at least I can help them. I created a note titled “Theme Editor” and started listing desired features. It should allow selecting a theme, listing files, editing them, and “somehow” saving without breaking anything. Then I continued writing, thinking out loud: > 👉🏻 It would be great to make this run as a patch to Ghost Admin, right inside it, as an open source project. But I see some challenges: > > \* File Manager is too complicated to maintain as an open source project (Maybe another project can be used) > \* Backend/UI should manage authentication with Ghost > \* Backend should handle different storage types, file system or S3 > \* Actually, it can be a client-side only library, fetches zip file, opens on client, edit, zip and upload. All on client side... Wow! That “wow” point was exciting. It was an “Aha!” moment. As a veteran web developer who spent his career on the frontend, I’ve always believed in the power of client-side apps. If it fits the requirement, client-side is the most scalable approach. And web apps are the most flexible. Downloading a zip file, decompressing it, editing it, compressing it again, and uploading the result looks challenging at first, but Web APIs and browsers have had these capabilities for years. I validated the idea with Claude. It pointed me to [JSZip](https://github.com/Stuk/jszip?ref=muratcorlu.com) for the hardest part. Then I asked it to create a detailed implementation plan. I already knew we can inject scripts into Ghost Admin via config. I also knew how theme download/upload works. I gave those constraints and asked for an implementation. It took around 30 minutes to get the first running version on my local Ghost instance. Editing was just a `textarea`, but the core functionality was there. Thankfully, it didn’t generate a React app, and no signs of Tailwind (I think it knows me well enough). Pure JS and CSS. After a few hours of iteration, I added [Monaco Editor](https://github.com/microsoft/monaco-editor?ref=muratcorlu.com) with syntax highlighting, file/folder create/rename/delete, and set up a GitHub repo and an npm package. > Monaco Editor is impressive. It leverages browser capabilities well, stays fast, and feels solid. And here is the result: [GitHub - synapsmedia/ghost-theme-editor: Edit theme files directly inside Ghost Admin with a completely client-side editorEdit theme files directly inside Ghost Admin with a completely client-side editor - synapsmedia/ghost-theme-editor![](https://cdn.synaps.media/muratcorlucom/content/images/icon/pinned-octocat-093da3e6fa40.svg)GitHubsynapsmedia![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/ghost-theme-editor)](https://github.com/synapsmedia/ghost-theme-editor?ref=muratcorlu.com) I like the result. - Way better than a platform-specific solution - No hacks on Ghost - No server-side work - Fast and secure And again, I’m impressed by the speed of building this with AI. I could write this app myself, but not in a few hours. I’m not an AI fanboy, but the potential is obvious—if you use it beyond vibe coding or generating trash content. That’s a separate topic. I hope the Ghost community and Synaps Media publishers will like this Theme Editor. Since it’s fully client-side (no server load), maybe the Ghost team can consider building something similar into Ghost Admin in the future, as well. I expect to have more updates on the extension soon, some polishing and features according to first feedbacks. Feel free to use and contribute. ### Fedi.pub - Create public profile links for your Ghost ActivityPub handle URL: https://muratcorlu.com/fedi-pub-create-public-profile-links-for-your-ghost-activitypub-handle/ Last updated: 2026-04-08T07:45:16.000Z I really like Social Web came with Ghost 6, and want to use it as my main Mastodon account. But one challenge was blocking this: How to share my address with people? In many places, I just share my Mastodon link, but there is no simple link that we can share for Ghost AP profiles. When surfing on other Ghost websites, I noticed that many people have this struggle. Since [I already spent some effort](https://muratcorlu.com/adding-ghost-social-web-notes-to-your-website-2/) on making to solve this, I thought it would be possible to make a simple profile page for all type of Ghost Social Web profiles. And here I introduce: [https://fedi.pub](https://fedi.pub/?ref=muratcorlu.com) ![](https://cdn.synaps.media/muratcorlucom/content/images/2025/12/screenshot-1600x1067.webp) Screenshot of my profile page on [fedi.pub/@murat@muratcorlu.com](https://fedi.pub/@murat@muratcorlu.com?ref=muratcorlu.com) With this simple site, you can generate (well, just put your handle to the end of URL) a public profile page for your Ghost Social Web handle. With a preview and a simple guidance to how to follow you, I think this can help many of us, to take the advantage of Ghost Social Web. Url format is like `https://fedi.pub/@murat@muratcorlu.com` ## How does it work It's a very simple tool written with [ExpressJS](https://expressjs.com/?ref=muratcorlu.com). Basically it uses my [ghost-activitypub-embed web component](https://github.com/synapsmedia/ghost-activitypub-embed?ref=muratcorlu.com) to show the profile page. On top of that, backend server fetches data for filling open-graph metadata, so your links can be seen better on social media, or in Ghost Bookmark Card. [Murat Çorlu - @murat@muratcorlu.comSoftware engineer and entrepreneur based in The Netherlands. Founder of synapsmedia.com, a Managed Hosting for Ghost CMS![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Fedi.pub![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/images-2Fd84ee3ff-07f3-495e-b8f2-47b385ef6e81-2F3fc8826f-4c7f-4a8a-a52f-100ff379fccb.jpeg)](https://fedi.pub/@murat@muratcorlu.com?ref=muratcorlu.com) Bookmark card example of a profile link Also Express backend is utilized as a proxy for the ActivityPub requests made by embed component. Normally, ActivityPub endpoints are designed to be accessible with cross domain requests. But because of special handling of `www` subdomains, I had to use this proxy solution. If you use `www` subdomain for your Ghost site, ActivityPub requests to your root domain are redirected to www subdomain. But this redirect responses are generally not CORS-friendly. If I can find a better solution to this issue, I want to make profile page a fully client-side solution. Currently it's hosted on [Synaps Media](https://www.synapsmedia.com/?ref=muratcorlu.com) servers with some help from Cloudflare. Also worth to mention: - This is (and will be) a free-to-use service. - I don’t collect any data from this tool. I don’t even have any trackers on the page. - The proxy used for AP requests doesn’t collect any information as well. - Client side code is [already open](https://github.com/synapsmedia/ghost-activitypub-embed?ref=muratcorlu.com), but I can share proxy code (which is a simple expressjs proxy) in the future. ## Some accounts to follow If you are interested in Ghost like me, here are a few accounts to follow: [Spectral Web Services - @cathy@spectralwebservices.comLet our team handle the technology, while you focus on creating. Ghost CMS experts.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Fedi.pub![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/witch-hat-icon_rev-1-01-3.png)](https://fedi.pub/@cathy@spectralwebservices.com?ref=muratcorlu.com) By far the best outside contributor of Ghost - Cathy Sarisky [Jannis Fedoruk-Betschki - @jannis@jannis.ioBuilding Magic Pages, a managed Ghost CMS hosting provider. This account is basically a very public test of ActivityPub on Magic Pages![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Fedi.pub![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/e4097517-2130-4149-9063-38d73ae9aa23.webp)](https://fedi.pub/@jannis@jannis.io?ref=muratcorlu.com) Founder of [Magic Pages](https://www.magicpages.co/?ref=muratcorlu.com), shares very valuable tools/information for Ghost [John O’Nolan - @john@john.onolan.orgFounder/CEO @ Ghost.org — Geographically restless. Publishing, open source, and independent business around the world.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Fedi.pub![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/gravatar-j10-square.jpg)](https://fedi.pub/@john@john.onolan.org?ref=muratcorlu.com) You probably already know him, but still... He is "the one" 😊 For more don't miss checking [Social Web section of Ghost Explore](https://explore.ghost.org/social-web?ref=muratcorlu.com). ## Thoughts about the future of ActivityPub in Ghost I'm very excited about ActivityPub integration in Ghost. Currently there are some missing parts but I can confidently say that, Ghost team is doing great job to improve it very quickly. The repository is very active. They are constantly fixing issues and adding new quality-of-life features. Ghost is investing on discoverability and interoperability more than any other ActivityPub platform, which is great. When Mastodon became viral, confusion over the loneliness of the instances was the biggest friction. But Ghost ActivityPub is promising even if you use it on your self-hosted AP server. You can still find any Fediverse account, see their latest posts even without following them. There are obviously some missing features, like attaching more images to a single post, attaching videos or hashtags, but I can also see a bigger potential specific to Ghost if I do some fantasy, like: - Showing fediverse mentions/reactions for a post under the article. This can help crowding our silent "comments" sections. - Separate accounts for each staff users, so authors can use it individually. If we return back to this tool; eventually fedi.pub would not be needed for Ghost in the future. Because I expect that Ghost will provide something like this in the future. But until that time I hope it will be helpful for some people anyway. Also, I'm considering to support any type of Fediverse handles in fedi.pub, in near future. So maybe people can use it as a "fediverse link sharing tool". We'll see... It’s a result of a few hours of work. There can be some quirks and bugs. So please give feedback if you find this useful, or if you miss some more features. ### Hidden Power of Bitwise Operators URL: https://muratcorlu.com/hidden-power-of-bitwise-operators/ Last updated: 2025-11-11T07:30:42.000Z Bitwise Operators are not very common to see in our daily codebases. Even it's sometime considered as bad-practice (because of reducing code readability). But still, in 2025, bitwise operators shine in some use-cases, especially the ones the performance matters a lot. Bitwise operators are often underutilized, but they can be very efficient for certain tasks. Here are some common **use cases** where bitwise operators can help in everyday coding tasks: ### 1\. **Checking if a number is even or odd** You can check if a number is even or odd by using the `&` (bitwise AND) operator. If the least significant bit (LSB) is 1, the number is odd; if it's 0, the number is even. #### Example: ```typescript function isOdd(n: number): boolean { return (n & 1) === 1; } ``` Checking if a number is even or odd ### 2\. **Swapping two numbers without using a temporary variable** You can use the XOR (`^`) operator to swap two numbers without a temporary variable. #### Example: ```typescript let a = 5, b = 3; a = a ^ b; b = a ^ b; // b becomes 5 a = a ^ b; // a becomes 3 // Now, a = 3 and b = 5 ``` Swapping two numbers without using a temporary variable ### 3\. **Clearing a specific bit** To clear a specific bit (set it to 0) in a number, you can use the bitwise `&` operator combined with a mask. #### Example: ```typescript let num = 14; // Binary: 1110 let mask = ~(1 << 2); // Binary: 1011 (2nd bit is 0) num = num & mask; // Binary: 1010 (clears 2nd bit) ``` Clear the 2nd bit (0-indexed) of a number ### 4\. **Setting a specific bit** To set a specific bit (make it 1), you can use the bitwise `|` operator. #### Example: ```typescript let num = 10; // Binary: 1010 let mask = 1 << 1; // Binary: 0010 num = num | mask; // Binary: 1010 (1st bit is already set) ``` Set the 1st bit (0-indexed) of a number ### 5\. **Flipping (toggling) a specific bit** To toggle a specific bit, use the XOR (`^`) operator. #### Example: ```typescript let num = 10; // Binary: 1010 let mask = 1 << 1; // Binary: 0010 num = num ^ mask; // Binary: 1000 (1st bit toggled) ``` Toggle the 1st bit (0-indexed) of a number ### 6\. **Counting the number of set bits (Hamming Weight)** You can count the number of 1s in the binary representation of a number using bitwise operators. #### Example: ```typescript function countSetBits(n: number): number { let count = 0; while (n) { count += n & 1; // Increment count if the least significant bit is 1 n >>= 1; // Shift the number to the right } return count; } ``` Counting the number of set bits ### 7\. **Checking if a number is a power of 2** A number is a power of 2 if it has exactly one bit set to 1 in its binary representation. This can be checked using the expression `n & (n - 1) == 0`. #### Example: ```typescript function isPowerOfTwo(n: number): boolean { return n > 0 && (n & (n - 1)) === 0; } ``` Checking if a number is a power of 2 ### 8\. **Finding the minimum or maximum of two numbers** You can find the minimum or maximum of two numbers without using conditional statements by using bitwise operators. #### Example: ```typescript function min(a: number, b: number): number { return b ^ ((a ^ b) & -(a < b)); } ``` Finding the minimum of two numbers ### 9\. **Quickly multiplying or dividing by powers of 2** Bitwise shifts (`<<` for left shift and `>>` for right shift) are efficient ways to multiply or divide numbers by powers of 2. #### Example: ```typescript let num = 5; let result = num << 2; // Equivalent to 5 * 4 = 20 ``` Multiply by 2^2 with bitwise shift ### 10\. **Finding the only non-repeating element in an array** As explained earlier, you can use XOR to find the element that appears only once in an array where all other elements appear twice. #### Example: ```typescript function findUnique(arr: number[]): number { let result = 0; for (const num of arr) { result ^= num; } return result; } ``` Finding the only non-repeating element in an arra ### 11\. **Getting the absolute value of a number** You can compute the absolute value of a number using bitwise operations. This involves finding the sign bit and conditionally inverting the number. #### Example: ```typescript function absoluteValue(n: number): number { const mask = n >> 31; // Get the sign bit (1 for negative, 0 for positive) return (n + mask) ^ mask; // If negative, flip bits and add 1 } ``` Getting the absolute value of a number ### 12\. **Checking if two integers have opposite signs** You can check if two integers have opposite signs using XOR. If the result of XOR is negative, the numbers have opposite signs. #### Example: ```typescript function haveOppositeSigns(a: number, b: number): boolean { return (a ^ b) < 0; } ``` Checking if two integers have opposite signs ### 13\. **Reversing the bits of a number** Bitwise operations can be used to reverse the bits in a number's binary representation. #### Example: ```typescript function reverseBits(n: number): number { let result = 0; while (n > 0) { result = (result << 1) | (n & 1); // Shift result left and add LSB of n n >>= 1; // Shift n right } return result; } ``` Reversing the bits of a number These are just some practical examples where bitwise operations can be highly efficient, especially in cases involving low-level optimizations, bit manipulations, or resource-constrained environments like embedded systems. ## Use-cases other than numbers Bitwise operators are typically used for manipulating numbers, but they can also be applied in creative ways for other data types or problems where data can be represented in binary form. Here are a few non-number-specific use cases where bitwise operators can be useful: ### 1\. **Boolean Arrays (Bitmasks)** Instead of using an array of booleans (`true`/`false`), you can use a single integer to represent the state of multiple booleans. This approach saves space and can be manipulated with bitwise operators. (Check Bonus section) #### Example: Storing the status of 5 switches (on/off) You can represent the on/off status of 5 switches as a bitmask where each bit represents a switch: - `1` \= on, `0` \= off. ```typescript let switches = 0; // All switches off (00000) // Turn on the first and third switches switches |= (1 << 0); // Turn on the first switch (00001) switches |= (1 << 2); // Turn on the third switch (00101) // Check if the second switch is on const isSecondSwitchOn = (switches & (1 << 1)) !== 0; // false // Turn off the third switch switches &= ~(1 << 2); // (00001) ``` Storing the status of 5 switches (on/off) This can be useful in games, permission systems, or toggling states of features. ### 2\. **Set Operations Using Bitwise Operators** You can use bitwise operators to simulate basic set operations (like union, intersection, and difference) when working with sets that can be mapped to integers. Each bit in a number can represent whether an element is present (1) or absent (0). #### Example: Set operations Consider sets represented as bitmasks, where each bit corresponds to an element. - Set A: `0101` (elements 1 and 3 are in the set) - Set B: `0011` (elements 1 and 2 are in the set) - **Union** (`A | B`): `0111` (elements 1, 2, and 3 are in the union) - **Intersection** (`A & B`): `0001` (element 1 is in the intersection) - **Difference** (`A & ~B`): `0100` (element 3 is in the difference) This can be useful in areas like: - **Feature flags**: Enabling or disabling features in software. - **Role-based access control**: Managing user permissions efficiently. ### 3\. **Character Manipulation** You can use bitwise operators to work with characters, particularly when manipulating or comparing ASCII values. Since characters are represented by integers (ASCII or Unicode), bitwise operations can be applied. #### Example: Convert a lowercase letter to uppercase In ASCII, lowercase letters and uppercase letters differ by a single bit (the 6th bit). You can toggle this bit to convert between cases. ```typescript function toUpperCase(char: string): string { return String.fromCharCode(char.charCodeAt(0) & ~32); } console.log(toUpperCase('a')); // Output: 'A' ``` Here, `& ~32` clears the 6th bit to convert any lowercase letter to uppercase. ### 4\. **Bitmap Image Processing** In image processing, pixels are often represented in binary, especially for monochrome (black and white) images where each pixel is a bit (1 for black, 0 for white). Bitwise operations are useful for manipulating such images. #### Example: Inverting colors in a bitmap If you have an image where each pixel is represented as a single bit (1 for black, 0 for white), you can invert the colors by flipping the bits. ```typescript function invertBitmap(bitmap: number): number { return ~bitmap; } ``` Inverting colors in a bitmap This would invert all bits in the binary representation of the image. ### 5\. **Efficient Permission Systems** Permissions in software (such as read, write, execute) can be efficiently stored as bits. Each permission can be represented as a bit in an integer, and you can use bitwise operators to check, grant, or revoke permissions. #### Example: Permission flags Imagine you have permission flags for a file: - Read: `1 << 0` (0001) - Write: `1 << 1` (0010) - Execute: `1 << 2` (0100) You can store permissions as an integer, and then use bitwise operators to check or modify the permissions. ```typescript let permissions = 0; // No permissions // Grant read and write permissions permissions |= (1 << 0); // Read (0001) permissions |= (1 << 1); // Write (0011) // Check if execute permission is granted const canExecute = (permissions & (1 << 2)) !== 0; // false ``` Permission flags ### 6\. **Graph Algorithms (Subset Manipulation)** In combinatorial problems, particularly those involving subsets, you can use bitwise operators to efficiently represent and manipulate subsets. #### Example: Enumerating all subsets of a set If you have a set of `N` elements, you can represent each subset as an `N`\-bit integer, where each bit indicates whether a corresponding element is included in the subset. ```typescript function generateSubsets(arr: number[]): number[][] { const subsets = []; const n = arr.length; const totalSubsets = 1 << n; // 2^n subsets for (let i = 0; i < totalSubsets; i++) { const subset = []; for (let j = 0; j < n; j++) { if (i & (1 << j)) { subset.push(arr[j]); } } subsets.push(subset); } return subsets; } console.log(generateSubsets([1, 2, 3])); // Output: [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]] ``` Enumerating all subsets of a set ### 7\. **Handling Multiple Boolean Flags** If you need to handle multiple boolean flags or options in a single variable, you can use bitwise operators to store them compactly in a single integer. #### Example: Storing multiple feature flags ```typescript const FLAG_A = 1 << 0; // 0001 const FLAG_B = 1 << 1; // 0010 const FLAG_C = 1 << 2; // 0100 let options = 0; // Enable FLAG_A and FLAG_C options |= FLAG_A; options |= FLAG_C; // Check if FLAG_B is enabled const isFlagBEnabled = (options & FLAG_B) !== 0; // false ``` Storing multiple feature flags ### 8\. **Compression Algorithms** Bitwise operations are heavily used in compression algorithms to encode and decode data efficiently. Huffman coding, for example, uses bits to represent variable-length codes for characters. ### 9\. **Cryptography** Bitwise operators are used extensively in cryptographic algorithms (e.g., AES, DES, SHA). They allow efficient manipulation of binary data, which is fundamental in encrypting and decrypting data. ### 10\. **Finite State Machines** Bitwise operations can be used to represent and manipulate states in a finite state machine. Each bit can represent a different state, allowing you to perform state transitions efficiently using bitwise operators. ## Bonus: Simple authorization with Bitwise Operators **Bitwise operators** are commonly used to implement **simple authorization checks** by using **bitmasks** to represent different permissions or roles. This technique is highly efficient because it allows multiple permissions to be stored in a single integer, and operations to check, grant, or revoke permissions can be performed using bitwise logic. ### Use Case: Authorization with Bitwise Operators Let's say you have a system where different permissions are represented by individual bits in a binary number. Each bit represents a particular permission, and you can combine multiple permissions into a single number. #### Example Permissions: - **Read**: `1 << 0` (Binary: `0001`, Decimal: `1`) - **Write**: `1 << 1` (Binary: `0010`, Decimal: `2`) - **Execute**: `1 << 2` (Binary: `0100`, Decimal: `4`) - **Delete**: `1 << 3` (Binary: `1000`, Decimal: `8`) A user can have any combination of these permissions by using a bitmask. For example: - If a user has read and execute permissions, their permission bitmask would be: `0001 | 0100 = 0101` (Decimal: `5`). ### Common Operations: 1. **Granting Permissions**: Use the **bitwise OR (`|`)** operator to add a permission. 2. **Revoking Permissions**: Use the **bitwise AND NOT (`& ~`)** operator to remove a permission. 3. **Checking Permissions**: Use the **bitwise AND (`&`)** operator to verify if a specific permission is granted. ### Example Code: ```typescript // Permission flags const READ = 1 << 0; // 0001 const WRITE = 1 << 1; // 0010 const EXECUTE = 1 << 2; // 0100 const DELETE = 1 << 3; // 1000 // Current user's permissions (initially no permissions) let userPermissions = 0; // Grant the user read and write permissions userPermissions |= READ; // 0001 userPermissions |= WRITE; // 0011 (both read and write now) // Check if the user has execute permission const canExecute = (userPermissions & EXECUTE) !== 0; // false // Grant execute permission userPermissions |= EXECUTE; // 0111 (read, write, and execute now) // Revoke write permission userPermissions &= ~WRITE; // 0101 (read and execute, but no write) // Check if the user has write permission const canWrite = (userPermissions & WRITE) !== 0; // false ``` ### Explanation of Operations: 1. **Granting Permissions**: - `userPermissions |= READ`: This sets the read bit to 1. - `userPermissions |= EXECUTE`: This sets the execute bit to 1 without affecting other permissions. 2. **Revoking Permissions**: - `userPermissions &= ~WRITE`: This clears the write bit by negating the mask (`~WRITE` is `1101`), then using AND to clear the write bit without touching other bits. 3. **Checking Permissions**: - `userPermissions & EXECUTE`: This checks if the execute bit is set (1). If it is, the result will be non-zero; otherwise, it will be 0. ### Use Case: Role-Based Access Control (RBAC) You can also use bitwise operations to handle **user roles** in a system. For instance, different roles (e.g., Admin, Editor, Viewer) can be represented as bits, and users can be assigned multiple roles using a single integer. #### Example Roles: - **Admin**: `1 << 0` (Binary: `0001`, Decimal: `1`) - **Editor**: `1 << 1` (Binary: `0010`, Decimal: `2`) - **Viewer**: `1 << 2` (Binary: `0100`, Decimal: `4`) You can combine roles and check them easily: ```typescript const ADMIN = 1 << 0; const EDITOR = 1 << 1; const VIEWER = 1 << 2; // Assign a user both Admin and Editor roles let userRoles = ADMIN | EDITOR; // Binary: 0011, Decimal: 3 // Check if the user is an Admin const isAdmin = (userRoles & ADMIN) !== 0; // true // Check if the user is a Viewer const isViewer = (userRoles & VIEWER) !== 0; // false // Grant Viewer role to the user userRoles |= VIEWER; // Binary: 0111 (Admin, Editor, and Viewer) // Revoke Editor role userRoles &= ~EDITOR; // Binary: 0101 (Admin and Viewer, no Editor) ``` ### Advantages of Using Bitwise Operators for Authorization: 1. **Space Efficiency**: You can store multiple permissions or roles in a single integer, saving space compared to arrays or objects. 2. **Performance**: Bitwise operations are extremely fast, often faster than other logical checks like multiple if-else conditions. 3. **Scalability**: You can easily add more permissions by adding more bits. For example, the 32-bit or 64-bit integer types allow you to store up to 32 or 64 permissions or roles. ### Conclusion Bitwise operations can be applied to a wide range of data types and problems beyond just numbers, especially when data can be represented in binary form (like sets, flags, or booleans). They provide an efficient and compact way to perform operations on data that would otherwise require more space or complex logic. **This article is written with the help of ChatGPT.* ### How many displays are enough to be a good developer? URL: https://muratcorlu.com/how-many-displays-are-enough-to-be-a-good-developer/ Last updated: 2025-11-07T15:19:38.000Z I had my first computer nearly 30 years ago. Since then I always desired to see more things on the screen at the same time. Because context-switching is a big problem for me. If I spend some time on something, I lost track on the previous one. Something should remember it to me. Most of the time, best approach was being able to see the other waiting tasks on my screen all the time. Because of that, multiple screen setups always seemed cool and great to me. Until a few years ago... Ergonomics while working is an increasingly important topic for me (I know, I'm getting old). A few years ago I had frozen neck and it was painful. I visited physiotherapist and he told me same old story about how to properly sit in front of the computer display. As you probably already now, you should look at your display directly, without needing to turn your head. But, how this will be possible if you have 2 (or more) screens? I tried to put my second monitor vertically, then above the main one, then at an angle, none of them worked. My eyes kept drifting, my neck kept twisting, and my focus scattered. What I realized was simple: I wasn’t using more screens to be productive; I was using them to avoid friction. The friction of closing something, reopening it, or deciding what actually mattered. Excluding the jobs that really need persistent visibility—like traders, sysadmins, or live ops engineers—most of us code on one thing at a time. You’re either reading, writing, debugging, or testing. Context switching doesn’t get easier with more displays; it just gets disguised. Every open window whispers for attention. 1. You always use one monitor as your main. Everything else is secondary clutter, even if you pretend it’s “for reference.” 2. Extra screens are distracting. They encourage multitasking instead of deep work. 3. The real problem isn’t screen space, it’s mental space. Then I returned back to single display setup. Just large enough to see a web page with developer tools and my code IDE side by side (I use 32" 4K display). Sometimes I use my iPad to check my notes while I'm making an online presentation by sharing my screen. That's it. Since around 3 years, I've been working with this setup and I feel pretty comfortable now. Even though filling the whole desk (or even the whole wall) with displays still looks cool, I don't see any practical advantage to it. So how many displays are enough to be a good developer? One. And even that one should have boundaries, full screen, no notifications, no Slack in the corner. Focus beats surface area every time. ### Adding Ghost Social Web notes to your website URL: https://muratcorlu.com/adding-ghost-social-web-notes-to-your-website-2/ Last updated: 2026-01-23T14:50:40.000Z When [Ghost released new Social Web feature](https://ghost.org/6/?ref=muratcorlu.com), one of the most common questions was "how to show our Notes on our Ghost site". Ghost didn't provide a built-in solution for this yet, but after noticing [Cathy's post about her solution](https://www.spectralwebservices.com/blog/a-sample-activitypub-feed/?ref=muratcorlu.com), I started investigating on that, and tried a solution that doesn't require extra resources or setup. As a result, I made [a simple web component](https://github.com/synapsmedia/ghost-activitypub-embed?ref=muratcorlu.com), and published on GitHub. It allows you to embed your latest notes on your Ghost Social Web to your pages or posts. ## How to use Using it quite simple. Create a page on Ghost, named like "Notes", and add an HTML card with the content below: ```html ``` With this simple code, you'll have something like on my site: ## How does it work? This component is running completely on client-side, by leveraging your website's already public ActivityPub endpoints to fetch your latest notes. So it works on all Ghost setups that runs Social Web, including self-hosted and managed hosting service like Ghost Pro, [Magic Pages](https://www.magicpages.co/?ref=muratcorlu.com) or [Synaps Media](https://www.synapsmedia.com/?ref=muratcorlu.com). It's a web component, not an iframe, so it doesn't actually run another page behind the scenes. And thanks to [underlying technology](https://developer.mozilla.org/en-US/docs/Web/API/Web%5Fcomponents/Using%5Fshadow%5FDOM?ref=muratcorlu.com) it's style isolated from the Ghost theme, but also customizable easily (coming later). In addition to using with HTML Cards, it's also possible to embed your social notes to your home page by editing your theme files. For example, if your theme has a sidebar, you can add same code to that sidebar part, which will list your notes in your home page as well. There are some limitations worth to mention: - This timeline doesn't list "re-posts" - Ghost normally automatically posts your latest articles. This timeline doesn't include those posts as well. - Currently it only shows latest 10 notes. ## What is next There are some features that I want to add soon: - ~~Optionally hiding header: This can help to show it inside templates with different type of content~~ - ~~Adding documentation about styling.~~ - ~~Show a dialog with the guidance for following your account on fediverse~~ - Loading more than 10 posts And there are some topic that I want to further investigate too: - Optionally showing re-posts - Showing like and re-post counts ## Edit (01/10/2025): CSS customization I just released 0.3.0 version by adding more CSS customization options and some documentation. Check [GitHub Readme](https://github.com/synapsmedia/ghost-activitypub-embed/?ref=muratcorlu.com) to see some examples. Additionally, I added it to my homepage: ![](https://cdn.synaps.media/muratcorlucom/content/images/2025/11/image.png) Here how it can be shown in sidebar of Source theme Adding this was very easy. Here is how to do it: 1. Find `partials/components/post-list.hbs` on your Source theme files. 2. Put the HTML code before `
    ` line: ```html

    Notes

    Follow me on fediverse with @muratcorlu@muratcorlu.com handle

    ``` 1. If you want to hide the profile header like I did, go to Code Injection on Ghost Admin and add this to Header section: ```html ``` That's it! ## Edit (14/12/2025): Fedi.pub I published another tool to share public profile links to others by using this component: [fedi.pub](https://fedi.pub/?ref=muratcorlu.com) Here you can find more information about it: [Fedi.pub - Create public profile links for your Ghost ActivityPub handleSharing Ghost ActivityPub links was tricky. Until now... Use fedi.pub to make your Fediverse account more reachable.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/murat-18.png)Web Development NotesMurat Çorlu![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/fedi.pub.webp)](https://muratcorlu.com/fedi-pub-create-public-profile-links-for-your-ghost-activitypub-handle/) --- If you find putting your notes to your site useful, please give this simple component a try and share your thoughts with me. Since [it's open source](https://github.com/synapsmedia/ghost-activitypub-embed/?ref=muratcorlu.com), I'm also open for code contribution as well. Thank you! ### A tribute to Squoosh and Icomoon URL: https://muratcorlu.com/a-tribute-to-squoosh-and-icomoon/ Last updated: 2025-06-06T14:57:15.000Z When I'm working on [the new landing page](https://www.synapsmedia.com/changelog/we-polished-our-landing-page/?ref=muratcorlu.com) of [Synaps Media](https://www.synapsmedia.com/?ref=muratcorlu.com), -again- I needed a few tools to do some tasks. I always find great tools, make the job and then forget it. When next project comes, I look for bookmarks, history and everywhere, but I always miss some of them. So this time, I decided to put some notes to a great place that I can't miss; my blog. ## Optimizing images with Squoosh [SquooshSquoosh is the ultimate image optimizer that allows you to compress and compare images with different codecs in your browser.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/icon-large-maskable-c2078ced.png)Squoosh![](https://cdn.synaps.media/muratcorlucom/content/images/thumbnail/icon-large-maskable-c2078ced.png)](https://squoosh.app/?ref=muratcorlu.com) This is a great tool. It works on browser, it works on client-side (means your images don't go anywhere), [it's open-source](https://github.com/GoogleChromeLabs/squoosh?ref=muratcorlu.com), it doesn't have any annoying ads, no-need to register and results are impressive. I converted many large png/jpeg images to webp and avif formats with it during this work. Definitely suggested! ## Icon works - Icomoon (still!) [SVG Icon Libraries and Custom Icon Font Organizer ❍ IcoMoonEasily mange your icons and integrate them in your projects. Browse free icons or import your own SVG icons to export as icon font, SVG, PNG, sprite and more.![](https://cdn.synaps.media/muratcorlucom/content/images/icon/apple-touch-icon-35f4a648bf9ba38347b4ab81eb7a06ba.png)](https://icomoon.io/?ref=muratcorlu.com) I used [Fontawesome](https://fontawesome.com/?ref=muratcorlu.com) for most of the icons in the page, but didn't want to import whole font library for just around 30 icons. First I tried to use SVG files directly, but it was too much work without any benefit over using font file for me. While looking for a tool to create a custom font, I came across to Icomoon. I remember using Icomoon maybe 10 years ago and surprised that it still works with same clear interface. I think it didn't have any updates since a while, so it didn't support WOFF2 format. But I easily converted TTF file to WOFF2 with another online tool named [CloudConvert](https://cloudconvert.com/ttf-to-woff2?ref=muratcorlu.com). So, instead of 300kb of resources to show about 30 icons, now I'm using **6kb**. That's a great! ## The tool I couldn't find I spent a lot of time to make this image: ![](https://cdn.synaps.media/muratcorlucom/content/images/2025/06/synaps-media-landing-on-devicas.jpg) I really thought, there should be an app or service that creates this automatically. Give a web address, and then get screenshots of it in some operating systems with realistic device frames. But I couldn't find any. Then I downloaded some device bezels from [Apple resources](https://developer.apple.com/design/resources/?ref=muratcorlu.com#product-bezels), got some screenshots from iOS and iPadOS simulators, put everything together in image editor (which I use [Affinity Designer](https://affinity.serif.com/en-us/designer/?ref=muratcorlu.com)). I liked the result but I still think I should not need to spend 3+ hours to achieve this very common thing (maybe a product idea?). Nevertheless, working on a pure frontend work, with a responsive, modern tech was fun. And I'm confident for both functional and technical results. [![](https://cdn.synaps.media/muratcorlucom/content/images/2025/06/image.png)](https://pagespeed.web.dev/analysis/https-www-synapsmedia-com/siexsinctj?form%5Ffactor=mobile&ref=muratcorlu.com) PageSpeed report for www.synapsmedia.com on mobile devices ### Pure CSS background image slides URL: https://muratcorlu.com/pure-css-background-image-slides/ Last updated: 2025-02-11T09:09:06.000Z I needed to show a list of images as background of header and cross-fading each other in time, like a slide show. My requirements were: - Images should be used as background images. Because images are only for visual enhancement, not a real data. 💡 Nowadays most people ignore [semantic HTML](https://en.wikipedia.org/wiki/Semantic%5FHTML?ref=muratcorlu.com), but I still care it. And that means, if you show an image just for adding some style to your page and if it doesn't add any meaning to your document, than you should avoid using `img` or `picture` tags. - Images should be lazy loaded. I should avoid fetching all of the slide images when page loaded. - Nice to have: It would be great to be able to define different formats and sizes for each slide to pick best for the client. Like [we do with ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source?ref=muratcorlu.com). There are many ways to make an image slider with or without getting help from CSS. But it's not common -or at least I couldn't see many examples- to have sliding those don't need an HTML markup. But after I played some with CSS animations, I realized that it's considerably easy to do that, except with some tricks (as always). Let's start with basics. For the simplicity, we'll just use `body` element to show rotating full-screen background images with a transition. We can fill our background with a photo like below: ```css body { background-size: cover; background-image: url('https://picsum.photos/900/400/'); } ``` If we convert this definition to a basic keyframe animation, it will still work. ```css body { background-size: cover; animation-name: slide; animation-fill-mode: both; } @keyframes slide { 0%,100% { background-image: url('https://picsum.photos/900/400/'); } } ``` Here we define an animation that just show an background image and finishes immediately. `animation-fill-mode: both` points that when animation ends, it'll stay its latest position. So now, what happens if we add one more slide to our animation and set sime time? Let's try: ```css body { background-size: cover; animation-name: slide; animation-fill-mode: both; animation-duration: 10s; animation-iteration-count: infinite; } @keyframes slide { 0%,49% { background-image: url('https://picsum.photos/900/400/?random=1'); } 50%,100% { background-image: url('https://picsum.photos/900/400/?random=2'); } } ``` Now I set a duration for the animation as 10 seconds. To loop the animation we set `animation-iteration-count: infinite`. And added one more image to the slides and sliced the total range by setting percentages, to show first image in first half, and second image in the second half. Now we already have 2 image background slides. That was easy, isn't it? We even covered our 2nd requirement, which was lazy loading. If you open the demo and check your developer tools network tab, you'll notice that second image will not be loaded until it's being showed. Cool! But that also causes a problem, right? Did you notice the flick? Since we load the image just when it's needed to be shown, and it takes some time to fetch the image, we see a white screen in between. How can we solve this? We somehown need to fetch the image before showing it. Luckily we are able to use multiple background images with CSS. And since we use `background-size: cover` , even if we use for multiple images, only one of them will be shown. I think we can use this. Let's check: ```css body { background-size: cover; animation-name: slide; animation-fill-mode: both; animation-duration: 15s; animation-iteration-count: infinite; } @keyframes slide { 0%,33% { background-image: url('https://picsum.photos/900/400/?random=1'), url('https://picsum.photos/900/400/?random=2'); } 34%,66% { background-image: url('https://picsum.photos/900/400/?random=2'), url('https://picsum.photos/900/400/?random=3'); } 67%,100% { background-image: url('https://picsum.photos/900/400/?random=3'); } } ``` Did you see it? It works! Now we fetch first 2 images immediately, and 3rd image when we show the 2\. slide. No flicker! If you don't want to fetch 2 images at the same time, you can even optimize more by adding another semi-step (like at 10%) to just fetch the 2\. image. Like: ```css ... @keyframes slide { 0%,10% { background-image: url('https://picsum.photos/900/400/?random=1'); } 11%,33% { background-image: url('https://picsum.photos/900/400/?random=1'), url('https://picsum.photos/900/400/?random=2'); } 34%,66% { background-image: url('https://picsum.photos/900/400/?random=2'), url('https://picsum.photos/900/400/?random=3'); } ... ``` OK, what's next? We need some transition, right? You maybe also be ok with that, but let's check how can add a cross-fading transition between the slides. You know what? It's actually already there but you can't see it. Let me explain: In our latest example we say we want an animation that will take 15 seconds. And we defined 3 slides in keyframes by pointing their positions with percentages. Percentages are like video slider positions. We define "keyframes" of our animation, and **browsers "animate" the values "between" them**. So we say "From 0% to 33%, background image will be this", and then "from 34% to 66% background image will be this". But what happens between %33 and %34? You guess right; animation happens there. Because that **gap is where browser does the animation**. But since total animation duration is 15 seconds, that means 1% takes only 150ms, which is so quick to notice. If we make those gaps bigger, then we'll able to notice the transition. If we make gaps 6%, transitions will be 900ms, nearly 1 second. ```css body { background-size: cover; animation-name: slide; animation-fill-mode: both; animation-duration: 15s; animation-iteration-count: infinite; } @keyframes slide { 0%,28% { background-image: url('https://picsum.photos/900/400/?random=1'), url('https://picsum.photos/900/400/?random=2'); } 34%,59% { background-image: url('https://picsum.photos/900/400/?random=2'), url('https://picsum.photos/900/400/?random=3'); } 67%,94% { background-image: url('https://picsum.photos/900/400/?random=3'); }, 100% { /* Transition to first slide */ background-image: url('https://picsum.photos/900/400/?random=1') } } ``` Nice! Now we have a cool cross-fade transition. If you want to try different transition effects (blur would be nice too), [backdrop-filter](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter?ref=muratcorlu.com) will be your friend. ## Bonus: Auto-select best image for the device No HTML markup? Check! Lazy loading? Check! Why do we stop here? Let's also check how can we provide best possible image for our visitor's device. What I mean? There are multiple very optimized image formats those browsers support: JPEG, AVIF, WEBP... AVIF and WEBP formats can reduce our total file size dramatically. But not all browsers support them. We need to check browser support and show the best option it can handle. 💡 Of course file type is not the only concern about providing the best image. Image resolution and density is other points that we need to consider. But those are already easily be covered with CSS `@media` queries. So I will not enter that topic in this article. Luckily, [image-set](https://developer.mozilla.org/en-US/docs/Web/CSS/image/image-set?ref=muratcorlu.com) is already winking to us. `image-set` is the `` of CSS. Its syntax is very easy: ```css body { background-image: image-set( url('https://placehold.co/600x400/webp?text=WEBP') type('image/webp'), url('https://placehold.co/600x400/jpg?text=JPEG') type('image/jpeg') ); } ``` Don't miss that order is important. The best option for you should be placed first and last one should be the fallback. Ah, speaking of fallback, we must not forget browsers that do not support `image-set`. Just place your fallback image before `image-set` definition like: ```css body { background-image: url('https://placehold.co/600x400/jpg?text=JPEG'); background-image: image-set( url('https://placehold.co/600x400/webp?text=WEBP') type('image/webp'), url('https://placehold.co/600x400/jpg?text=JPEG') type('image/jpeg') ); } ``` Now, we are able to define our images with multiple types. Latest code will be a bit crowded. Something like: ```css @keyframes slide-show { 0%,17% { background-image: url('slide1.jpg'), url('slide2.jpg') ; background-image: image-set( url('slide1.avif') type('image/avif'), url('slide1.webp') type('image/webp'), url('slide1.jpg') type('image/jpeg') ), image-set( url('slide2.avif') type('image/avif'), url('slide2.webp') type('image/webp'), url('slide2.jpg') type('image/jpeg') ); } 20%,37% { background-image: url('slide2.jpg'), url('slide3.jpg'); background-image: image-set( url('slide2.avif') type('image/avif'), url('slide2.webp') type('image/webp'), url('slide2.jpg') type('image/jpeg') ), image-set( url('slide3.avif') type('image/avif'), url('slide3.webp') type('image/webp'), url('slide3.jpg') type('image/jpeg') ); } ... ``` 💡 Are you looking for a tool to optimize and convert your images? Check [https://squoosh.app](https://squoosh.app/?ref=muratcorlu.com) It looks crowded but I loved the result. It's fast, it's good and it's cheap. But I also see this as a start. Modern CSS provides unlimited opportunities to us and all of them are waiting us to discover them. I hope this tutorial helped you. What do you think? Is it really that good, or am I just overreacting? Do you have more points to improve it? Please share your thoughts! ### Docker Healthcheck without curl or wget URL: https://muratcorlu.com/docker-healthcheck-without-curl-or-wget/ Last updated: 2024-10-19T22:29:16.000Z I use [official Docker image of Ghost](https://hub.docker.com/%5F/ghost?ref=muratcorlu.com) to deploy my Ghost websites. I use docker swarm for orchestration and I need to have a healtcheck to have zero-downtime upgrades. Common way of doing this using `curl` to make a request to a convenient URL to check if Ghost is running, like below: ```yml healthcheck: test: "curl -H 'X-Forwarded-Proto: https' -If http://localhost:2368/ghost/api/admin/site/ || exit 1" interval: 10s timeout: 10s retries: 10 ``` 💡 I use `X-Forwarded-Proto` header to mimic a reverse proxy to bypass Ghost's SSL redirection. The problem is, official Ghost Docker image doesn't include `curl`, or `wget` command. But it has NodeJS for sure (because Ghost runs on NodeJS). So instead of creating my own Docker image just to have `curl` command for health check, I decided to write a command in node to have same result. ```yml healthcheck: test: "node -e 'require(\"http\").get({host: \"localhost\", port: 2368, path: \"/ghost/api/admin/site/\", headers: {\"X-Forwarded-Proto\": \"https\"}}, res => process.exit(res.statusCode === 200 ? 0 : 1)).on(\"error\", () => process.exit(1));'" interval: 10s timeout: 10s retries: 10 ``` It's a little longer but it makes the job. Basically, this command runs the JavaScript code below as a single line command. ```js require("http").get({ host: "localhost", port: 2368, path: "/ghost/api/admin/site/", headers: { "X-Forwarded-Proto": "https" } }, res => process.exit( res.statusCode === 200 ? 0 : 1 ) ).on("error", () => process.exit(1) ); ``` This approach can be also used in Docker images other than JavaScript. As far as you can make an HTTP call from command line with the programming language that your docker image includes, it'll handle the task. For example, with Python: ```yml healthcheck: test: "python3 -c 'import http.client; conn = http.client.HTTPConnection(\"localhost\", 2368); headers = {\"Custom-Header\": \"your-header-value\"}; conn.request(\"GET\", \"/\", headers=headers); res = conn.getresponse(); exit(0) if res.status == 200 else exit(1)'" ``` Hope this helps for some! ### Design Systems URL: https://muratcorlu.com/design-systems/ Last updated: 2024-10-14T12:17:30.000Z I have been busy working on Design Systems since I started working on [Baklava](https://github.com/trendyol/baklava?ref=muratcorlu.com), open-source design system that we developed at Trendyol. Working on an open-source design system in an enterprise company has many challenges. And checking good examples is one of the best opportunities you would have. I left Trendyol a while ago but still very interested in with design systems. In this post I wanted to collect a list of handpicked design systems. I'll update this post with new discoveries in the future. So keep refreshing 😊 ## Government Design Systems Design Systems are perfect match for digital government works, since it's naturally expected to have a common, high quality and highli accessible digital services from governments. Here are some public government design systems: [NL Design System - homepage | NL Design SystemNL Design System samenwerken aan componenten en richtlijnen voor een begrijpelijke, gebruiksvriendelijke én toegankelijke online dienstverlening![](https://static.ghost.org/v5.0.0/images/link-icon.svg)NL Design System![](https://raw.githubusercontent.com/nl-design-system/documentatie/assets/meedoen__utrecht.svg)](https://nldesignsystem.nl/?ref=muratcorlu.com) Design System of The Netherlands [Home – GOV.UK Design SystemDesign your service using GOV.UK styles, components and patterns![](https://static.ghost.org/v5.0.0/images/link-icon.svg)![](https://design-system.service.gov.uk/assets/images/govuk-opengraph-image.png)](https://design-system.service.gov.uk/?ref=muratcorlu.com) [USWDS: The United States Web Design SystemUSWDS makes it easier to build accessible, mobile-friendly government websites.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)U.S. Web Design System (USWDS)![](https://designsystem.digital.gov/img/uswds-logo/lg-black.png)](https://designsystem.digital.gov/?ref=muratcorlu.com) ## Brand Design Systems Either for just using their own products or for creating an ecosystem for outside developers, big companies are also interested in working on open-source design systems. Here are some good examples: [Material DesignBuild beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Material Design![](https://m3.material.io/static/assets/m3-social.jpg)](https://m3.material.io/?ref=muratcorlu.com) Maybe one of the most popular design systems of the world. Material from Google [Human Interface Guidelines | Apple Developer DocumentationThe HIG contains guidance and best practices that can help you design a great experience for any Apple platform.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Apple Developer Documentation![](https://docs.developer.apple.com/tutorials/developer-og.jpg)](https://developer.apple.com/design/human-interface-guidelines?ref=muratcorlu.com) It's provided in a one of a kind by Apple, but still it's very inspiring for sure [Fluent 2 Design SystemExplore the next evolution of Microsoft’s design system, enabling more seamless collaboration and creativity than ever. Move fluidly from design to development, between apps, and across platforms.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Fluent 2 Design System![](https://fluent2websitecdn.azureedge.net/cdn/fluent-logo-metadata.Cwa-_8F7.png)](https://fluent2.microsoft.design/?ref=muratcorlu.com) Microsoft's Design System [https://spectrum.adobe.com](https://spectrum.adobe.com/?ref=muratcorlu.com) \- Adobe's Spectrum Design System [Carbon Design SystemCarbon is IBM’s open source design system for products and digital experiences. With the IBM Design Language as its foundation, the system consists of working code, design tools and resources, human interface guidelines, and a vibrant community of contributors.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Anna Wen![](https://carbondesignsystem.com/ogimage.png)](https://carbondesignsystem.com/?ref=muratcorlu.com) [Lightning Design System![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Design System![](https://www.lightningdesignsystem.com/assets/images/homepage/fun-component-blueprints.png)](https://www.lightningdesignsystem.com/?ref=muratcorlu.com) Design System of Salesforce [Overview - Get started - Atlassian Design SystemStart designing and developing beautiful product experiences with Atlassian Design System UI foundations, components, and standards.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Atlassian Design System![](https://atlassian.design/static/b2197a58813457540a5f2d27b3362869/meta.png)](https://atlassian.design/?ref=muratcorlu.com) [GitHub - Trendyol/baklava: Baklava is a design system provided by Trendyol to create a consistent UI/UX for app users.Baklava is a design system provided by Trendyol to create a consistent UI/UX for app users. - Trendyol/baklava![](https://static.ghost.org/v5.0.0/images/link-icon.svg)GitHubTrendyol![](https://opengraph.githubassets.com/ce5bf7d5b0252957224b7755c1d111d27330636c0b8d02a82c9dd7f4da463a8a/Trendyol/baklava)](https://github.com/trendyol/baklava?ref=muratcorlu.com) [Nord Design SystemDesign, build, and ship coherent experiences with Nordhealth’s design system.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Nord Design SystemNordhealth![](https://nordhealth.design/img/opengraph-v4.png)](https://nordhealth.design/?ref=muratcorlu.com) ## Events, blogs and other resources [Brad Frost WebBrad Frost is a web designer, speaker, consultant, musician and artist living in beautiful Pittsburgh, PA.![](https://static.ghost.org/v5.0.0/images/link-icon.svg)Brad Frost![](https://bradfrost.com/wp-content/uploads/2017/08/card-image.png)](https://bradfrost.com/?ref=muratcorlu.com) Author of Atomic Design book and an important blog for design systems [SmashingConf in-person Conferences 2024 — Friendly, inclusive & fun events for web designers and developers.Looking for a practical conference about web design and development? We organize three hybrid conferences in 2024: Freiburg, NYC & Antwerp. Get your ticket now! Front-end, design, UX and much more!![](https://static.ghost.org/v5.0.0/images/link-icon.svg)SmashingConf![](https://smashingconf.com/images/events/og/og.png)](https://smashingconf.com/conferences?ref=muratcorlu.com) Smashing Conferences include many talks about Design Systems [Design Systems Week 2024 | NL Design SystemNL Design System organiseert dit jaar voor de 4e keer de Design Systems Week. Van 14 tot en met 17 oktober zijn er![](https://static.ghost.org/v5.0.0/images/link-icon.svg)NL Design System![](https://raw.githubusercontent.com/nl-design-system/documentatie/assets/dsw-24.png)](https://nldesignsystem.nl/events/design-systems-week-2024/?ref=muratcorlu.com) Yearly conferences from NL Design System team. Includes English talks as well ### #TIL - Reddit provides RSS feeds URL: https://muratcorlu.com/til-reddit-provides-rss-feeds/ Last updated: 2026-02-26T09:28:42.000Z ![](https://cdn.synaps.media/muratcorlucom/content/images/2024/02/image.png) News Explorer on macOS After big drama on Twitter last year, I stopped using it and moved to Mastodon for microblogging (~~murat@synaps.space~~ [@murat@muratcorlu.com](https://fedi.pub/@murat@muratcorlu.com?ref=muratcorlu.com)). But I then realised that I was learning a lot from the tweets from the people that I follow (most of them left Twitter anyway). Some friends suggested to use Reddit for following "topics" instead of people. That sounded nice and gave Reddit a try but same issue of all social media channels disturbed me again: "auto curated timeline". I follow only a few channels but in my timeline I have many unrelated things because I just had a look one of the post on that channel (probably I came there from a search result), or Reddit thinks it's related with my other channels, or it's just an ad. I was nearly giving up but then I noticed [RSS support of Reddit](https://www.reddit.com/wiki/rss/?ref=muratcorlu.com). I love RSS. [My RSS reader](https://betamagic.nl/products/newsexplorer.html?ref=muratcorlu.com) is my own social media. I have lots of RSS feeds, always waiting for me, in a categorised and date ordered feed, for the time that I prefer. I don't miss anything if I skip checking my RSS reader for a few days. I want to write more about how I love and use RSS in a separate article. So I added some of the Reddit channels to my RSS reader. And now it works perfect. If I want to check news/posts about a topic that I'm interested in, I just go to that category in my RSS reader, and see a combined list of posts about that topic from Reddit, some blogs, Github release notes etc. The first thing I would recommend to everyone to increase productivity and efficiency would be to start using RSS. ### #TIL - envsubst command URL: https://muratcorlu.com/til-envsubst-command/ Last updated: 2024-03-25T08:24:07.000Z I was doing some bashscript work to generate yaml files from simple templates to be able to use in GitLab CI jobs. First thing that I did was using `sed` command and replaced variables in yml like this: ```bash sed "s/\${SERVICE_DOMAIN}/$SERVICE_DOMAIN/g; s/\${SERVICE_NAME}/$SERVICE_NAME/g" template.yml ``` Replacing some variables inside a file with `sed` command This works but when you add more variables and especially if values includes some chars that breaks regex rule, then it's not helpful anymore. Then I found awesome [envsubst](https://www.gnu.org/software/gettext/manual/html%5Fnode/envsubst-Invocation.html?ref=muratcorlu.com) command in [a stackoverflow thread](https://stackoverflow.com/a/11050943/198062?ref=muratcorlu.com). ```bash $ cat template.yml services: ${SERVICE_NAME}: environment: url: ${MAIN_URL} ... $ MAIN_URL=https://muratcorlu.com $ SERVICE_NAME=muratcorlu $ export MAIN_URL SERVICE_NAME $ cat template.yml | envsubst services: muratcorlu: environment: url: https://muratcorlu.com ... ``` Using `envsubst` command to replace env variables inside a file This is so much cleaner! I loved it! ### #TIL - Plot by D3 creators URL: https://muratcorlu.com/til-plot-by-d3-creators/ Last updated: 2024-02-28T21:50:20.000Z Years passes but my search for a simple and good chart library was always there. Today, I -again- came to the point to write my chart with D3.js but then I met with Plot. [Observable PlotThe JavaScript library for exploratory data visualization![](https://static.observablehq.com/favicon-512.0667824687f99c942a02e06e2db1a060911da0bf3606671676a255b1cf97b4fe.png)Observable![](https://static.observableusercontent.com/thumbnail/64f414fef8a91248865f5759641b0cf537bc87c0aaf57dc368ffe673013eccaa.jpg)](https://observablehq.com/plot/?ref=muratcorlu.com) Plot is chart library built on top of and by the creators of D3\. Comparing to many chart libraries, creating simple, good looking charting libraries are surprisingly easy. It's hard to believe but a simple area chart can be drawn as easy as this single line: ```js Plot.areaY([4, 8, 15, 16, 23, 42]).plot(); ``` And the great part is, it's still D3 and SVG. So you can always let CSS step in for quick solutions. I'm exploring the details but I'm already in. ### #TIL - Docker based static site hosting solutions URL: https://muratcorlu.com/til-docker-based-static-site-hosting-solutions/ Last updated: 2024-02-21T18:18:47.000Z As a frontend developer, I deployed hundreds of static website projects until now on tens of different providers including GitHub/GitLab pages, Netlify, Surge.sh, Firebase, AWS S3 (+ Cloudfront) and also my very own [domain to path redirected Nginx server](https://gist.github.com/muratcorlu/bae35db86429684f018d4c9f0b6123ca?ref=muratcorlu.com). They are all good services but either expensive or limited for some use-cases. Nowadays my favorite is Cloudflare Pages but main downside of it, basically you need to move your domain to Cloudflare to get the full advantage of Pages. Cloudflare DNS (and CDN) is also a great and -mostly- free service, so no problem for your own websites. But if you consider to deploy autogenerated static websites for a SaaS project, then you have some challenges. Today I was having a look for good self-hosted solutions and noticed [Meli](https://github.com/getmeli/meli?ref=muratcorlu.com) on a [Reddit post](https://www.reddit.com/r/devops/comments/kb5l6m/meli%5Fa%5Fnetlifylike%5Fplatform%5Ffor%5Fdeploying%5Fstatic/?ref=muratcorlu.com). [GitHub - getmeli/meli: Platform for deploying static sites and frontend applications easily. Automatic SSL, deploy previews, reverse proxy, and more.Platform for deploying static sites and frontend applications easily. Automatic SSL, deploy previews, reverse proxy, and more. - getmeli/meli![](https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg)GitHubgetmeli![](https://opengraph.githubassets.com/e1b279ad7213faa8d3a07a9c5d4b856e8117148b0950b314a321f329d22ee26f/getmeli/meli)](https://github.com/getmeli/meli?ref=muratcorlu.com) It looks very nice! It targets to really replace a static hosting service like Netlify. It has a UI, a CLI and many configuration options. It looks unmaintained anymore dough (I even considered to maintain it). It uses [Caddy](https://caddyserver.com/?ref=muratcorlu.com) as the HTTP server, which is the one I always want to try but no chance yet. Apparently [it can work](https://github.com/getmeli/meli-docs/blob/latest/docs/configuration/reverse-proxy.md?ref=muratcorlu.com#traefik) with Docker Swarm behind [Traefik](https://traefik.io/?ref=muratcorlu.com), which is also important for me. Other one that I could find was [staticdeploy](https://github.com/staticdeploy/staticdeploy?ref=muratcorlu.com). [GitHub - staticdeploy/staticdeploy: Open-Source Platform for Deploying Static AppsOpen-Source Platform for Deploying Static Apps. Contribute to staticdeploy/staticdeploy development by creating an account on GitHub.![](https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg)GitHubstaticdeploy![](https://repository-images.githubusercontent.com/111467353/eaea8c80-dde5-11e9-8382-8b52d8fb26ce)](https://github.com/staticdeploy/staticdeploy?ref=muratcorlu.com) It seems like the development of this project also stopped 3 years ago as Meli (Something must have happened about this matter 3 years ago. 😊 ) This project doesn't have a UI but still have good configuration options, a CLI helper. I'll try both projects and give it a try. Maybe I can write my thoughts later in a separate article. BONUS: I also came across such a fun article while researching this topic: [The smallest Docker image to serve static websitesThe smallest Docker image to serve static websites![](https://lipanski.com/favicon.ico)Florin Lipan![](https://lipanski.com/assets/images/logo.jpg)](https://lipanski.com/posts/smallest-docker-image-static-website?ref=muratcorlu.com) ### #TIL - Op3.dev URL: https://muratcorlu.com/til-op3-dev/ Last updated: 2024-02-19T15:36:33.000Z [OP3: The Open Podcast Prefix Project](https://op3.dev/?ref=muratcorlu.com) A project that can intervene your podcast media files and generates public download stats by completely free. Apparently it works with the support of some sponsors and they also share [their Cloudflare invoices](https://op3.dev/costs?ref=muratcorlu.com) (It's around $700/m currently). I would expect that it also proxies the media files to provide automatic CDN support since Cloudflare is already very generous about the traffic if you are a paid customer. ### #TIL - ZeroSSL URL: https://muratcorlu.com/til-zerossl/ Last updated: 2024-04-05T22:45:17.000Z [Free SSL Certificates and SSL Tools - ZeroSSLFree SSL certificates issued instantly online, supporting ACME clients, SSL monitoring, quick validation and automated SSL renewal via ZeroSSL Bot or REST API.![](https://zerossl.com/assets/images/zerossl_icon.png)ZeroSSL LogoZeroSSL![](https://zerossl.com/assets/images/zerossl_logo.svg)](https://zerossl.com/?fpr=muratcorlu&ref=muratcorlu.com) I use LetsEncrypt for all of my projects since a while. But this week I learned that, LetsEncrypt has a limit of 50 domain certificates generation per week. > The main limit is **Certificates per Registered Domain** (50 per week). A registered domain is, generally speaking, the part of the domain you purchased from your domain name registrar. For instance, in the name `www.example.com`, the registered domain is `example.com`. > [https://letsencrypt.org/docs/rate-limits/](https://letsencrypt.org/docs/rate-limits/?ref=muratcorlu.com) This is still a generous number but if you consider to provide a SaaS with custom domain support, you would need to consider a solution. ZeroSSL can be a good drop-in replacement for LetsEncrypt since it also supports ACME clients. So it works with Traefik, that was important to me. And $8/m for basic requirements seems like a fair pricing. ### EDIT (5 April 2024): I wanted to try ZeroSSL buy subscribing their Basic Plan. When I enabled on my Traefik Server, I immediately started getting "429 Too Many Requests" errors from their server. They don't mention any rate limits anywhere and I only tried to issue certificates for a few domains. Then I noticed that this seems like a common problem on ZeroSSL. Check this GitHub Issue: [Controller can’t handle hitting request rate limits of zerossl ACME API · Issue #5867 · cert-manager/cert-managerDescribe the bug: We’ve been using cert-manager with zerossl as ACME provider using http01 challenges for several months now vey successfully. However, since a couple of weeks ago, zerossl must hav…![](https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg)GitHubcert-manager![](https://opengraph.githubassets.com/7e8e50eee5a89e5aa5b4e74e39a6e2e8ce1be6aab4f17f307b7235111704f59c/cert-manager/cert-manager/issues/5867)](https://github.com/cert-manager/cert-manager/issues/5867?ref=muratcorlu.com) So, please be cautious for using ZeroSSL to avoid LetsEncrypt's rate limits. You can consider using [Google Cloud's ACME](https://cloud.google.com/blog/products/identity-security/automate-public-certificate-lifecycle-management-via--acme-client-api?ref=muratcorlu.com). ### Health check for Ghost Docker deployment URL: https://muratcorlu.com/health-check-for-ghost-docker-deployment/ Last updated: 2024-02-17T17:46:34.000Z I'm playing with Ghost for a while to host many websites as easy as possible. I'm a fan of Docker Swarm to manage a production environment that I can host many projects in an easy way. (For more details how to do that, you can check [great articles from my friend Uğur Aslan](https://uguraslan.net/tag/docker/?ref=muratcorlu.com)) Ghost team officially doesn't provide a Docker image. But there is a popular community-driven Docker project. When you want to deploy a Ghost website via Docker, you will notice a short downtime (around a few seconds) while updating the container. With Docker Swarm deploy configuration, you can assure that first running a new instance and then stop the previous version. ```yml services: mysite: image: ghost:latest environment: url: https://mysite.com volumes: - settings:/var/lib/ghost/content/settings - themes:/var/lib/ghost/content/themes - data:/var/lib/ghost/content/data deploy: replicas: 1 update_config: parallelism: 1 order: start-first failure_action: rollback delay: 20s rollback_config: parallelism: 0 order: stop-first restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s ``` With this config, we order that a new container will be run in parallel, will wait for 20s and **if there is no failure** then previous container will be stopped. If there is a failure, a rollback will be executed to the previous version. But the problem is, sometime initialisation of Ghost instance can take more time. Ghost does DB migrations, cache filling etc. So we need to really be sure that our new instance is healthy. Fortunately, Docker gives us a chance to check health of running instance by running a command inside the container. Basic and most popular way of doing that check is doing a curl request to a URL. If that URL responds well, than we assume that container is healthy. Generally, services provides a way to check system health. Like a command or a lightweight HTTP endpoint. Ghost doesn't provide an endpoint specifically but [it's suggested to use an Admin API endpoint](https://github.com/TryGhost/Ghost/pull/13117?ref=muratcorlu.com) which doesn't require authentication and returns basic information about the running website (which is a good functionality for a healthcheck). ```yml services: mysite: ... healthcheck: test: "curl -H 'X-Forwarded-Proto: https' -If http://localhost:2368/ghost/api/admin/site || exit 1" interval: 30s timeout: 10s retries: 5 ``` With this simple addition, we make a http request to running Ghost internally, and accept a 200 response. `-f` parameter of curl assures to have a failure if we don't have a successful response. And `|| exit 1` makes ending this whole command successful if there is no error happens with curl. Other properties are very obvious: "Try running this command up to 5 times with 30 seconds intervals and wait command to finish up to 10 seconds". You may noticed `X-Forwarded-Proto: https` part. By default, if Ghost is configured to run using SSL, all of the http requests are redirected to https equivalent. Since this is a "failure" for our curl command and we can not call https://localhost easily, the workaround is adding `X-Forwarded-Proto: https` header which directs that TLS is handled by a proxy, so Ghost consider this request as a secure one. You may also question why we just don't call the production URL, like `curl -f https://mysite.com/ghost/api/admin/site` . This may also work for most of the times but there can be many issues causes to fail this request which is not related to our container's health. SSL may be expired or not renewed yet, DNS can be faulty, main ghost url could be changed etc. Especially, if you auto-generate SSL certificates while your Docker instance running, it's most likely this healthcheck will fail and your SSL could never be generated. To be brief, if you want to deploy a Ghost websites via Docker, don't miss adding a healthcheck, and here you have a running configuration. ### CSS Day 2023 Notes URL: https://muratcorlu.com/css-day-2023-notes/ Last updated: 2026-05-12T19:10:05.000Z I’m quite happy that I could join [CSSDay conference](https://cssday.nl/?ref=muratcorlu.com) this year in person. This conference is the only one that focuses on CSS. Since I really love CSS and currently working on [a design system](https://github.com/Trendyol/baklava?ref=muratcorlu.com), it was a huge chance for me to meet some very cool people and ask their opinions about many topics that gnaw at my mind. ## Talks Here are my remarks about the talks: ### Una Kravets - State of the CSS Community This was a very inspiring talk that highlight how CSS got powerful in time but yet still very underrated. I got her call very seriously for being more active on creating content about CSS, organizing more CSS focused events. ### Hidde de Vries - Dialogs & Popovers This topic was already on top of my agenda since we talk a lot on our dialog and popover components nowadays in Baklava Design System. I’m already very excited for popover element. Hopefully we’ll able to use in our design system before end of this year. Differences and similarities of dialog and popover elements was a big question mark for me and this talk helped me put things in my mind properly. Using popover element with different aria roles like “dialog” (why not), tooltip, menu or listbox flashed the light. I learned he is also working on [a design system](https://nldesignsystem.nl/?ref=muratcorlu.com) and we had a nice chat over design systems later in break sessions. (I’ll have a section about those talks related with design system below) His presentation: [https://talks.hiddedevries.nl/00ZIVb](https://talks.hiddedevries.nl/00ZIVb?ref=muratcorlu.com) ### Léonie Watson - CSS Speech Of course I was not even aware of [CSS Speech Module](https://drafts.csswg.org/css-speech-1/?ref=muratcorlu.com), since it’s not even implemented in any browser. This was an interesting and eye-opener talk for me. [Why we need CSS Speech - Tink - Léonie Watson![](https://tink.uk/favicons/apple-touch-icon.png)Tink - Léonie Watson - On technology, food & life in the digital age](https://tink.uk/why-we-need-css-speech/?ref=muratcorlu.com) ### Sanne ‘t Hooft - Tinkerer by Night This was a fun talk that Sahne (as a designer) shared many experiments he did with CSS. I would like to work with a designer who loves to play with CSS that much. 😊 Here is the full list of his experiments: [https://sinds1971.nl/cssday](https://sinds1971.nl/cssday?ref=muratcorlu.com) ### Patrick Brosset - Selector Performance All “CSS Selector Performance” talks always reminds that, “you probably don’t need to optimize your CSS selectors”. This was not an exception. But he remarked interesting CSS performance stories related with browser bugs and how to debug them in some good tooling like Firefox Profiler, “Style invalidation tracking” feature of Chrome dev tools and “advanced rendering instrumentation” in Edge selector stats. ### Heydon Pickering - Scope in CSS This was absolutely a big fun. It was more like a standup show than a tech talk. But also very on point: “CSS is already scoped, from the beginning”. Very inspiring! ### Sophie Koonin - Personal Websites I’m thinking about the importance of having personal websites a lot since for a while and this talk was a big motivation for me to come back to that idea and do some investment for my website(s). There were many inspirational personal web site examples in the talk. ### Miriam Suzanne - Container Queries It was very nice to hear many details of how container queries work and “why” it work like that, by story of how they are defined. Especially with style queries, I think container queries will change how we write CSS for components a lot. It was especially very inspiring for me in terms of web component styling in a design system. ### Umar Hansa - Tooling & Worklows I’m generally not very into the tricks of using an IDE efficiently, but this talk was a pack of very cool tips & tricks that triggered me try using them right after. I got so much notes, I’ll definitely check most of them. ### Stephanie Eckles - Building Components When I see “component”, I think of web components directly but this was not about it. Talk was focusing on using new CSS features to style a component -as a concept- effectively and efficiently. Her presentation: [https://noti.st/st3ph/ea40FC/modern-css-for-dynamic-component-based-architecture](https://noti.st/st3ph/ea40FC/modern-css-for-dynamic-component-based-architecture?ref=muratcorlu.com) ### Jhey Tompkins - Creative Coding There was a -really- working timer in this talk that is written with pure CSS. And I’m amazed how Jhey used CSS anchoring for many smart use-cases. ### Cassondra Roberts - Styling web components This was a introduction level talk about web components and tricks about styling them. Because, I'm already working on this since 1 year, nothing was new for me. But we had a nice chat after her talk about her opinions and experience over design systems (check below). (She is working on Adobe's [Spectrum Design System](https://spectrum.adobe.com/?ref=muratcorlu.com)) ### Bramus Van Damme - Scroll Driven Animations & View Transitions That was a super good summary of what we have and can expect of those 2 super new features coming to CSS. Hopefully they will arrive browsers other than chrome soon. [Scroll-driven AnimationsA bunch of demos and tools to show off Scroll-driven Animations![](https://scroll-driven-animations.style/favicon.svg)![](https://scroll-driven-animations.style/social.jpg)](https://scroll-driven-animations.style/?ref=muratcorlu.com) ### Manuel Matuzovic - Structuring & destructuring It was great to finish the conference with an another inspiring talk from Manuel. He has a remarkable talent for storytelling, and his insights on the effective utilization of custom properties were thought-provoking and motivating. ## Chatting over Design Systems and Open-Source It was wonderful that I had the opportunity to engage in conversations with experienced professionals like [Una](https://twitter.com/Una?ref=muratcorlu.com), [Hidde](https://twitter.com/hdv@front-end.social?ref=muratcorlu.com), [Adam](https://twitter.com/argyleink?ref=muratcorlu.com), [Cassondra](https://front-end.social/@castastrophe?ref=muratcorlu.com), and [Jeremy](https://mastodon.social/@adactio?ref=muratcorlu.com) who have expertise in managing design systems and open-source projects in large companies. Sometimes, finding comprehensive resources on these specific topics can be challenging, so having the chance to personally connect with individuals who have firsthand experience is truly invaluable. Here are some of my remark: - Managing design system is hard for even large companies. I'm pretty sure now, most of the challenges that I have in my company is not specific to us. - There are some sad stories behind the scenes of the open-source projects owned by large companies. "Marketing" is a big -if it's not the biggest- motivation to make a project open-source for a company. "Giving back to open-source" or "taking advantage of the wisdom of public" are not mentioned so much. - Managing a design system in a large company is people issue, more than a technical challenge. - I'm surprised when I heard how many people works on design systems projects on companies like Google and Adobe. (It's way more than my expectation) - When we talk about convincing the people who resists for investing on a design system, Jeremy said something very smart: > “Every company has a design system even if they are not aware of it. You may like or not, but design system is how you do your work in a company.” So, combining "what we have now" and using this as the starting point of the discussion with the questions like "are we happy with this?", "what are the bad parts?", "how can we solve those issues?" would be a good way of going forward. I liked this idea! ## Inspiration This was a first offline conference that I joined after a while. I remembered how important to have chance to talk with the people in-person. Also the energy that you get from a conference is very different than listening to online talks. Here are my intents greening after this conference: - Retry to finish first working version of my [11ty Zen Garden ](https://github.com/muratcorlu/11ty-zen-garden?ref=muratcorlu.com)project - Renovating my blog with a micro blogging style with view transitions (like App Store, I love transitions at App Store app) - Starting a series named "X days of web components" (Inspired form "[100 Days Of More Or Less Modern CSS](https://www.matuzo.at/blog/2022/100-days-of-more-or-less-modern-css/?ref=muratcorlu.com)") - Apply to more conferences for my talk proposal titled "Why Design Systems Fail?" - Checking opportunities to organize a CSS focused meetup in Eindhoven ## About the organization Conference was in [Zuiderkerk](https://en.wikipedia.org/wiki/Zuiderkerk?ref=muratcorlu.com) in Amsterdam, which is an old abandoned church. This is the second time that I join a conference here since [I gave a talk](https://www.youtube.com/watch?v=yF%5F8O4l-Ybc&t=27s&ref=muratcorlu.com) in same place 5 years ago in JSNation. I like the ambiance of this place. I think this conference was also very well organized. Drinks, meal options, sticking to schedule was all good. I believe recordings of the talks will be also published soon in Youtube channel. ### CSS isolation with Declarative Shadow DOM URL: https://muratcorlu.com/css-isolation-with-declarative-shadow-dom/ Last updated: 2023-03-30T08:42:27.000Z With the release of [Safari 16.4](https://webkit.org/blog/13966/webkit-features-in-safari-16-4/?ref=muratcorlu.com), browser support for Declarative Shadow DOM has become widespread. DSD (Declarative Shadow DOM), is a powerful tool, particularly for serializing Web Components to make them compatible with server-side rendering (SSR) environments. However, I see another practical use case for it, which may appeal to larger audiences: using it for style isolation of page elements. When I worked at sahibinden.com, a leading classified website in Turkey, it was a significant challenge to ensure that classified descriptions remained consistent while still being stylable by users. Users could copy styled content from their websites, complete with unique styles and layouts, and expect to see acceptable results on our classified detail page. Since the description portion of the page was already styled by our CSS definitions, there was a risk of breaking our UIs due to the styles injected into the classified description. As a result, we were doing so much work to keep that page consistent regardless of the style of the classified description. On those times (it was 6-7 years ago), we were even considering to use iframe to isolate those free-style descriptions from our own HTML content. But even years after, there is no easy solution to address this issue, except ShadowDOM. Let's consider that we have a product description content within our `
    ` tag like below: ```html

    Don't miss this

    This product is best in its class.

    ``` As you can see we already encapsualted styles of the custom styles of the product description by auto-generating some unique (fingers crossed) class names for them. However, we also know that these tags are still affected by our main CSS styles. If we have CSS rule like `main h3 { margin-left: 36px; }` this will apply description too. The ugly solution would be to set every possible styles for every possible elements inside description with `!important` to be sure that they can not be overridden (fingers crossed again), but I believe now we have a better option. ```html
    ``` When you run this, you don't need to give any extra effort to create unique class names or override every possible style rules with `!important`. And now this works on latest Chrome and Safari without extra JS at all. Check the running example! What are the benefits we have here?: 1. Less effort. Just putting user-entered content with their styles inside a template with shadowroot. That's it. 2. Global styles don't break description content with custom styles. 3. Styles inside shadow dom won't apply outside of it. So no risk to break your page. 4. Since styles are now applied with smaller selectors and to a smaller DOM, they are more performant. ([https://www.youtube.com/watch?v=nWcexTnvIKI](https://www.youtube.com/watch?v=nWcexTnvIKI&ref=muratcorlu.com)) Any caveats? 1. You need to run a -small- [pollyfill](https://developer.chrome.com/articles/declarative-shadow-dom/?ref=muratcorlu.com#polyfill) to cover older browsers. 2. Some old bots can miss the content inside template tag. Not that much, huh? I hope you found this idea inspiring and see the potential benefits of leveraging this API. Such flexible APIs give web developers superpowers, and I appreciate how quickly browser vendors have been responding to developer requests in recent years (thanks to the [Interop](https://wpt.fyi/interop-2023?ref=muratcorlu.com) initiative). Hopefully, the web will experience a new acceleration with the help of these new cross-browser features. ### Mocha.js equivalent of it.each URL: https://muratcorlu.com/mocha-js-equivalent-of-it-each/ Last updated: 2023-03-08T15:22:50.000Z I was working with [web-test-runner](https://modern-web.dev/guides/test-runner/getting-started/?ref=muratcorlu.com) to write some tests for our a Web Component in our Baklava Design System. There was some repeating tests checking similar use-cases with different key codes. I wanted to use `it.each` to simplify the test like below: ```js it.each(["Space", "Enter", "ArrowDown", "ArrowUp"])( "should open popover with %i key", async (keyCode) => { // given await sendKeys({ press: "Tab", }); await sendKeys({ press: keyCode, }); // then expect(blSelect?.opened).to.equal(true); } ); ``` But, [WTR uses Mochajs](https://modern-web.dev/docs/test-runner/test-frameworks/mocha/?ref=muratcorlu.com) as the testing framework and Mocha doesn't have `it.each`. I tried using [a plugin](https://www.npmjs.com/package/mocha-each?ref=muratcorlu.com) and then tried to use Jest with WTR but both had some drawbacks. Then I realized it's actually [easier than expected](https://mochajs.org/?ref=muratcorlu.com#dynamically-generating-tests): ```js ["Space", "Enter", "ArrowDown", "ArrowUp"].forEach((keyCode) => { it(`should open popover with ${keyCode} key`, async () => { // given await sendKeys({ press: "Tab", }); await sendKeys({ press: keyCode, }); // then expect(blSelect?.opened).to.equal(true); }); }); ``` This took more than one hour for me. I hope this will help for some people to not loose that time again. ### Front-end may soon need new sub-disciplines URL: https://muratcorlu.com/front-end-may-soon-need-new-sub-disciplines/ Last updated: 2024-11-20T09:32:25.000Z Backend effort on most projects is declining every day with the help of high abstraction by cloud services. We can already make complete client-side applications with dynamic and collaborated data by just using some public services (like [Firebase](https://firebase.google.com/?ref=muratcorlu.com) or [CloudKit](https://developer.apple.com/icloud/cloudkit/?ref=muratcorlu.com)). We can manage [authentication](https://auth0.com/?ref=muratcorlu.com) without having any server to maintain. Many authentication providers and even some authentication services allow us to do that. And obviously, things will not stop here. It’s not hard to imagine that we’ll have more database services where we can directly write/read data from our front-ends securely and efficiently. The backend work of most of the projects will be just connecting and configuring multiple cloud services to each other. So, will every web developer be a “front-end developer”? Of course, there will always be a need for more low-level server-side specialists, at least for creating those cloud services. But maybe 90% of the coding effort for web projects will be done on the client side. And this will mean the “front-end” will have more and more new responsibilities and challenges. We can already see those with the new web APIs every day we start to hear, like Web Assembly, Web Worker, Web GPU, etc. The work we’ll begin to handle those new layers will be not just “basic UI” things. Instead, we’ll move some new stuff to the “front-end” that we previously handled on our backends with the help of the new power we’ll have in browsers. Take Web Assembly, for example. Web Assembly allows us to run some libraries in the browser with native performance. You cannot (and intend to) do DOM manipulations inside the Web Assembly layer. Still, you can make very CPU-intensive things like image processing, data cryptography, or even maybe Machine Learning. Web Workers gives us to run some other JavaScript routines outside of our render cycles without blocking UI. WebGPU will give us the full power of GPU to make even more efficient graph calculations, crypto calculations, or 3D renderings. WebRTC allows it to create a peer-to-peer connection with another user of our client app and send/receive any data, including streaming video or audio. File System Access API gives full access to the local file system of the user to read/write files and directories. These are not very familiar things that we do daily as front-end developers. In the meantime, real UI work effort is not getting less. Creating durable, reusable, flexible, easy-to-use, and accessible components is still a big challenge. Providing the best UI with the best UX needs neat work with designers and product owners. As a result, I expect we’ll need to consider splitting those new responsibilities inside web client-side development. Let me name them “Web UI” and “Web Core.” ## Web UI Developers Maybe we can roughly list the responsibilities of Web UI developers as follows: - Web Components - CSS - Routing - Forms - Animations - Accessibility - Web Fonts - Canvas - SVG, SVG filters These are the items that we are very familiar with as front-end developers. They all are directly related to UI and user interaction. And we all know that if you consider being a specialist in all of those areas and being able to create a very well-implemented web application, there will be many challenges waiting for us. This area is also where our popular web frameworks are responsible. We make components with Lit, manage complex forms with Angular, or create responsive grids with Tailwind. (The last one was a joke) ## Web Core Developers Web Core developers may be mainly responsible for the business logic and service-like backend part of the client app. These things will not directly change UI but primarily data management, business logic, and scalability (very familiar things for current backend developers, right?). If we want to list some technology names for Web Core developers, we may record: - WebGL - WebGPU - Worker - Web Assembly - Web Crypto - IndexedDB - File System Access API - WebRTC There is no React or Angular here. Nothing here for them. But for complex web applications, those APIs are like treasures. Also, asking WebGPU or WebAssembly expertise from a senior React developer will not be fair. ## Example project team structure Let’s consider a project as a photo/video management web app. We’ll make a web application to add our photos and videos to a library, and edit, categorize or share them. We’ll not have a backend code for this. We’ll use a database and a file storage service, which can work with an authentication service. Those services will be fully-managed, external services. We’ll handle image/video editing entirely on the client site. And we’ll store all data on our storage services in an e2e encrypted way. As you can see, there are pretty many things to do for both UI and “Core” sides in this project. For UI: - A fancy UI to list, search and add many photos and videos to the library. - A fully-featured and easy-to-use UI for editing images and videos - Share albums with others, comment on photos - Making this app “accessible” as much as possible On the other side, there are also huge challenges on the core side: - Writing some “Web Workers” to communicate with our cloud services in a non-blocking manner - Writing a very efficient image manipulation library - Writing a very efficient video editing library - An API to crypt/decrypt images and videos before sending them to our storage service and after fetching them - Storing some part of the data locally to make our app run offline and sync with services later When we list our tasks like this, I think separating the types of tasks becomes very clear. “Web Core” tasks are pure IO challenges that never touch DOM or CSS. On the other hand, the APIs that the “Web Core” team will provide to the “Web UI” team will be very similar to what we currently have with the “Frontend - Backend” world. JS interfaces replace REST or GraphQL APIs. ## Wrapping up Titles and disciplines are not big walls to keep developers in their responsibilities forever. Instead, they are helpers focusing on one thing at a time. Distraction is one of the biggest diseases of our time. And I think it’s very valuable for every IT professional to focus on quality, not quantity. ### History of Web Icons URL: https://muratcorlu.com/history-of-web-icons/ Last updated: 2022-06-23T22:03:32.000Z When I started web development, we -as webmasters- structured our web page layouts with `table` elements. It may seem very stupid now, but that looked like a brilliant idea when I learned HTML. So, if you want to show a disk image next to the “Save” label inside a button, it was very likely to see a code something like this: ```html ``` By having better CSS positioning support in browsers, we simplified HTML a lot and used CSS background with a gif image and some padding tweak for the same purpose. ```html ``` ```css .save-button { padding-left: 16px; background: url(save.gif) left center; } ``` Since this disk image doesn’t add any value to the content and just for improving the visual representation of the button to make it easy to see and understand, we hide it from HTML and set it on CSS. Cleaning all of the decorative elements from HTML was very smart. Because; - Internet was slow, and HTML is not cached in the browser. - HTML was the data layer, and we all knew that a decorative icon should not be placed in HTML. But there was a problem with that approach; we started to have many icons and making separate HTTP requests for all those GIF images was very time-consuming in HTTP/1\. Some smart people thought that we could merge all icons to a single image and show icons in the correct place by clipping with background positioning. We put all of the icons we have to a single GIF(or PNG) image in a vertical canvas by putting some safe space between them. Then just adding the line below to our CSS saved the day. ```css .save-icon { ... background-position: 0 -768px; } ``` But that couldn’t go long since relying on element sizes was not safe. Then some people found a hybrid solution. ```html ``` ```css .save-icon { display: block; float: left; width: 10px; height: 10px; background: url(icons.gif) 48px 125px; } ``` It may annoy you (it should) that we put an empty element to the document just to place a background image there. But luckily some later, CSS made her move and added “content” property together with `before` and `after` pseudo elements. By waiting some time for having browser support we reached previous simplicity in HTML. ```html ``` ```css .save-button:before { content: ''; display: block; float: left; width: 10px; height: 10px; background: url(icons.png) 48px 125px; } ``` Then some smart people said, “since we can give text content in CSS and we have some icon fonts already, why not use font icons with CSS?” If designers are ok with using single color icons -and they were all happy enough with that- font icons were a brilliant solution. Because they are vectors, they are single files, file size is small, and it was even possible to do some minor customizations with CSS, like color or weight. So we bought this idea very quickly. ```css .save-button:before { content: '\u3252'; font-family: 'my-icon-font'; } ``` Even some people remembered the ligature feature of fonts and we managed to write icon names as CSS content values like: ```css .save-button:before { content: 'save'; font-family: 'my-icon-font'; } ``` Font icons had some rendering issues because of different approaches browsers were using to render fonts, but in terms of accessibility, there was no issue until that time. Because those icons were decorative things, and they were not mentioned in HTML. Also, CSS-generated content was not red by screen readers. But probably because many developers misused CSS-generated content by adding actual content to the pages via CSS, screen readers started reading CSS-generated content too. And this caused an accessibility issue for showing decorative icons with font icons. So apparently set of misuses and wrong fixes made CSS content property a less useful tool. To hide the font icons from screen readers, you need to add aria-hidden=“true” to the container element. But since we don’t have a container element just for the icon, this was not a solution for us. Then this returns us to the previous step again. You add an empty element just for being able to hide it from screen readers. If you check [fontawesome accessibility guideline](https://fontawesome.com/v5/docs/web/other-topics/accessibility?ref=muratcorlu.com), you will see those kinds of examples. So our example returned to this: ```html ``` After that point, SVG came to the scene. SVG is a vector graphic format and can be used as an external image in CSS or directly embedded in HTML. SVG is better rendered on browsers compared to fonts, it has multicolor support, it can be animated, and even highly customized by CSS. Inlining SVG icons in HTML is getting more and more popular nowadays. Even "icon components" are generally part of every UI library. Because seeing that we are convicted to have a specific DOM element just for showing an icon, why not use an SVG directly inside HTML? But, we had a rule, remember: Decorative images should not be placed in HTML. And actually, we don't have big arguments to leave that rule behind. Still keeping HTML for "data-only" makes sense. You may say "in React we don't use HTML anymore, we directly create DOM". But once SSR comes to the picture, that DOM becomes HTML in our response, so no difference again. You may ask, why it's that much problem to include icons in HTML. We can just add `aria-hidden="true"` to prevent screen-readers to read it. That's true but it's not just about screen-readers. - External images and styles can be easily cached in the browser. Inlining an SVG in HTML makes it slower to download and it's not easy or reasonable to cache HTML in the browser most of the time. - Decorative icons are part of our theme and should be easily changed regarding screen estate, dark/light mode, or a theme selection for the user. With CSS these are trivial. But if you make icons a part of your templates, you have so much unneeded complexity in your JS. Inlining SVGs are actually very powerful if it's not decorative, and part of your document, like charts. So you can put a dynamic chart to your document with SVG and you can style it with CSS. That's great. So, what is the ideal way to "not" embed SVGs in HTML for decorative icons? If we are talking about something decorative, actually CSS is (should) be the only solution. If you don’t need to style an SVG icon with CSS, using external SVG as CSS background is still a good option. Even though [it’s possible to change color of an SVG background with CSS](https://css-tricks.com/solved-with-css-colorizing-svg-backgrounds/?ref=muratcorlu.com), it’s not a very easy-to-use solution for every use-case. Another approach is fetching SVG file with JS within a component. This is the approach we used in our [Baklava Web Component Library](https://github.com/Trendyol/grace/tree/next/src/components/icon?ref=muratcorlu.com). In this approach -in an icon component- you are lazily fetching external SVG file and embedding it to HTML to take the advantage of styling SVG with CSS and not embedding them to our templates directly. This is hybrid solution though. We are still actually embedding SVG to latest HTML in use, but we do it lazily. For me, decorative icons in web is still a non-fixed issue. I’m still looking for an ideal solution to put decorative visuals to our Web Designs without needing to disturb our HTML. ### A productivity tip: Automate first! URL: https://muratcorlu.com/a-productivity-tip-automate-first/ Last updated: 2021-12-12T14:34:30.000Z In recent years I have developed a lot of projects on various scales. Some of these have been developed and deployed very quickly. But maintaining these projects were not as fun as developing them. While I’m trying to fix issues or adding new features to my projects, I notice that the boring part for me is not the development phase. Generally, it’s about being ready for development and publishing the work. I’ll define with a simple example: Let’s assume a button doesn’t show up while I am navigating my project on a mobile device. When I saw that issue, I understand the technical problem behind it immediately, even with being aware of the place of the problem by source file and the line number. The solution could be as easy as adding a line like `position: absolute;` to a style file. But, to make this fix, I need several mandatory steps: 1. Sit in front of your computer. 2. Add a line to a CSS file. 3. Test it. 4. Commit your change. 5. Build your project. 6. Deploy it to your server. And I usually postpone fixing the code, considering these steps. And then my projects are having many small issues and my motivation falls. During the last years, I’ve embraced automation in order to transform these steps into a useful process far from being tedious. The approach, which is very popular as Continuous Integration (CI) and/or Continuous Delivery, is to automate as much as possible the process outside of doing the job. However, in general, this is not considered as a requirement for simple hobby projects, although it is considered to be a requirement for complex projects or large teams in terms of developers, and is regarded as unnecessary time loss. Last year I decided to change my mindset about that and I’ve started to automate each project beginning from the “hello world” phase. When I started to deploy my projects to the production automatically with every commit to my master branch and deploy them to a testing URL with every commit to my develop branch, my development flow changed a lot. Firstly Bitbucket’s [Pipelines](https://bitbucket.org/product/features/pipelines?ref=muratcorlu.com) feature and then Gitlab’s [Gitlab CI](https://about.gitlab.com/features/gitlab-ci-cd/?ref=muratcorlu.com) feature helped me a lot to come to that mindset. Both of the Pipelines and Gitlab CI are free with a reasonable quota for your private repositories. For example, Gitlab gives you 2,000 minutes of Gitlab CI for free and this is way more than enough for many small size development teams. Last 1 year, after I started to work with Gitlab CI, my new flow for fixing something became something like that: 1. Find and edit the file on Gitlab’s web UI. 2. Save it. There is no 3\. step! Because, build, test and deploy steps are already defined and handled by my Gitlab CI configuration. And I can see the result in a few minutes in my mobile browser. If it’s a risky fix for my project, I usually prefer to fix and commit the change in the develop branch. In this case, my change is automatically publishing to a testing environment and after being sure it’s working very well, I’m just merging develop to master to have it in production. That’s it! After switching to automation approach, whenever I saw a problem about my project at home, at a cafe, on the way, at a park or somewhere else, I’m just fixing it without thinking any boring part of the process. And after I noticed the big motivation gain of that approach, I strongly suggest using that style of development to every of my developer friends. Like we have “mobile first”, “test first” or “customer first”, I think we should also have a new slogan: “Automate first” ### Mocking REST endpoints in web apps, the easy way URL: https://muratcorlu.com/mocking-rest-endpoints-in-web-apps-the-easy-way/ Last updated: 2023-03-08T15:24:24.000Z # Why we need mocks? At one of my previous companies, in the early times, we had a flow like that: - A feature implementation is created by design team - Designs come to front-end team(us). We create static HTML pages, then task goes to back-end team. - Back-end team makes HTML dynamic. - Then probably because of we need some re-work and some extra JavaScript codes, the task returns back to front-end team. - Then task goes testing team… After we changed our architecture as a Single Page Application and started to use REST API as the back-end, we wanted to make that flow faster and decided to use client side mocking solutions. The idea was to change our flow like that: - Design comes to front-end team - Front-end and back-end team decides endpoint structures that they will need for that task. - Front-end team uses mocks and starts implementation without waiting for real endpoint - Back-end team tries to create that endpoint. When endpoint is ready, front-end team doesn’t need any change for release. That means, you can ****develop your task by parallel** with your back-end team. This will be improve your productivity and you will not have lot’s of troubles about your back-end dependencies. This would be also good for ****reproducing interesting bugs**. Sometimes we had some very exceptional but also annoying issues that are very hard to reproduce with real data. With an opportunity like defining API responses in a simple way, it would be very simple to create some awkward situations for testing. Another advantage would have a development environment that ****doesn’t really need to connect some remote servers, even internet**. You would not need a working REST API server to work. Then we created ****connect-api-mocker** # What is connect-api-mocker? [connect-api-mocker](https://www.npmjs.com/package/connect-api-mocker?ref=muratcorlu.com) is an npm package(actually a simple connect middleware) that can be used for creating fake responses for REST endpoints. We created the first version in 2013 at one of my previous companies and also [shared on Github](https://github.com/sahibinden/connect-api-mocker?ref=muratcorlu.com). After I left there, I [forked the repository](https://github.com/muratcorlu/connect-api-mocker?ref=muratcorlu.com) and continued to improve it to use it on my clients’ projects. I also created [an npm package](https://www.npmjs.com/package/connect-api-mocker?ref=muratcorlu.com) and added many features. connect-api-mocker, uses some mock json files in filesystem that are created with same folder structure with REST endpoints. This makes it very simple to use and modify it. But it’s not just that, there are more! # How is it easy to use mocking You can use connect-api-mocker with Webpack, Grunt, Gulp and other [connect.js](https://github.com/senchalabs/connect?ref=muratcorlu.com) powered environments. You can install connect-api-mocker with command: npm i --save-dev connect-api-mocker To use it with a webpack project, you need to use `setup` property of `webpack-dev-server`: ```javascript var apiMocker = require(‘connect-api-mocker’); ... setup: function(app) { app.use('/api', apiMocker('mocks/api')); },... ``` That configuration means, api mocker will try to respond requests that has url starts with `/api` by some files in the `mocks/api` folder. For example, if you make a `GET` request to `/api/users/1` it will respond that request with file located in`mocks/api/users/1/GET.json`. You already got the idea, right? The request mapping structure is like that: ****\[METHOD\] /{baseUrl}/{path} > {basePath}/{path}/\[METHOD\].json** It will work all kind of request methods like `POST`, `PUT`, `DELETE`, `OPTIONS`etc. Some examples: POST /api/users/1 ==> mocks/api/users/1/POST.json DELETE /api/users/1 ==> mocks/api/users/1/DELETE.json JSON files are normal, simple JSON files. # Custom responses Creating mock json files with same path by request path is a good idea, but it’s not enough for all cases. If you also want to create some different mock responses for query or other request parameters, you will need more. This is where custom response support comes in. Custom responses are simple connect middleware functions that also created in same path except(and of course) with a JS file extension. For example, instead of using simple `POST.json` file, you can create a `POST.js`file with content like that: ```javascript module.exports = function (request, response) { if (!request.get('X-Auth-Key')) { response.status(403).send({}); } else { response.sendFile('POST.json', {root: __dirname}); } } ``` If you place that file to `mocks/api/users/POST.js` and if you make a `POST`request to `/api/users`, api-mocker will run your custom response middleware instead of returning `POST.json` file in the path. In the custom response example above, we are testing existence of a header and if it’s not there we are responding with a `403` error, if it’s there, we are simply sending `POST.json` file content in the same folder. Can you see the power of that functions? ****Another example:** GET /api/users?type=active ==> mocks/api/users/****GET.js** ```javascript module.exports = function (request, response) { var targetFileName = 'GET.json'; // Check is a type parameter exist if (request.query.type) { // Generate a new targetfilename with that type parameter targetFileName = 'GET_' + request.query.type + '.json'; // If file does not exist then respond with 404 header if (!fs.accessSync(targetFileName)) { return response.status(404); } } // Respond with targetFileName response.sendFile(targetFileName, {root: __dirname}); } ``` With an example like above, we can send separate JSON responses depending on a query parameter. And another example with another good opportunity: GET /api/gateway/3213132 ==> mocks/api/gateway/3213132/GET.js ```javascript var requestCount = 0; module.exports = function (request, response, next) { requestCount++; if (requestCount == 1) { response.json({ status: 'notready' }); } else { response.json({ status: 'ready' }); } }; ``` Because of connect servers are running as an instance, we can store some in-memory variables like the example above. In that example we are responding different result depending of request count to that endpoint. As you can see, you don’t have any limit with custom responses! # Wildcard support We need one more thing: defining some wildcards in path definitions. We need that because it’s not very cool to create folders for every ids of our models like `users/1`, `users/2`, `users/3` etc. What is about to create o folder like `users/__user_id__` and use that `user_id` variable in our custom responses as a request parameter? Yeah, that would be great! The good news is it’s already there! So if you make a request something like that: GET /api/projects/****50b3c102–298a-45ad-acad-e21b6c1bbdcc/** You can define a mock in `mocks/api/projects/50b3c102-298a-45ad-acad-e21b6c1bbdcc/GET.json` If api mocker finds a file with that path, it will respond with that. But another option is creating a file in a path like `mocks/api/projects/__project_uuid__/GET.json`. In that case, all requests to `projects/{something}` that has not a specific mock file, will be responded with same response. Also it’s possible to define a custom response in the same folder with a `GET.js` file and produce a dynamic response depend on wildcard variable that defined in folder name( `project_uuid` ). For example: ```js module.exports = function (request, response) { response.json({ id: request.params.project_uuid }); } ``` Wildcard definitions are starting and ending with double underscore. And it’s the last that api mocker checked. So looking for a mock response flow is like that: 1. look for `mocks/api/projects/50b3c102-298a-45ad-acad-e21b6c1bbdcc/GET.js` 2. look for `mocks/api/projects/50b3c102-298a-45ad-acad-e21b6c1bbdcc/GET.json` 3. look for `mocks/api/projects/__{something}__/GET.js` 4. look for `mocks/api/projects/__{something}__/GET.json` Also you can define multiple nested wildcards for a path like: `mocks/api/__resource_type__/__resource_uuid__/GET.json` . That will be respond all GET requests to `api/*/*` . # NextOnNotFound setting By default, if you enable api mocker for a path and you don’t define a mock file for a request, it will be respond with a 404 Not Found error. But it’s also available to set `nextOnNotFound` to `true` then all not found requests will be passed to `next` middleware. Sample configuration: ```js app.use('/api', apiMocker({ target: 'mocks/api', nextOnNotFound: true }); ``` This is very powerful if you only want to mock some part of your endpoints instead of all of them. With the help of proxy features of [Webpack](https://webpack.github.io/docs/webpack-dev-server.html?ref=muratcorlu.com#proxy), [Grunt](https://github.com/drewzboto/grunt-connect-proxy?ref=muratcorlu.com) or Gulp, you can continue to get responses from your real api that you don’t want to mock. An example for webpack: ```js // webpack.config.js ... setup: function (app) { app.use('/backend/api/v1/', apiMocker({ target: 'mocks/api', nextOnNotFound: true })); app.use('/analytics/api/v1/', apiMocker({ target: 'mocks/analytics', nextOnNotFound: true })); }, proxy: { '/backend': { target: 'https://api.backend.yourdomain.com' }, '/analytics': { target: 'https://api.analytics.yourdomain.com' } } ``` In the example above, we defined 2 api mocker for 2 separate paths and also defined some proxies to real api servers for that paths. With the help of `nextOnNotFound` option, requests to that paths that doesn’t have a mock definition will be passed to that proxies. # Conclusion I used connect-api-mocker nearly on all my projects for last 4–5 years with several teams and we were very happy for getting advantages of it. I also suggest to use it or some solutions like that in your projects to simplify and make fast your development environment. Because of [it’s also open source](https://github.com/muratcorlu/connect-api-mocker?ref=muratcorlu.com), you are very welcome for your contributions. [https://github.com/muratcorlu/connect-api-mocker](https://github.com/muratcorlu/connect-api-mocker?ref=muratcorlu.com) Do you have an idea or a problem with api mocker? Just [create an issue](https://github.com/muratcorlu/connect-api-mocker/issues/new?ref=muratcorlu.com) on Github repository and let’s talk about it!