Dūwi is a skincare brand started by scientists. In June 2024 my freelance team designed a new store for them in Figma, and I built the theme. We launched in July.
The founders are not developers, and after the handover they would run the store alone. So I set one rule for myself before writing any Liquid: nothing on the home page is hard-coded. If the founders can see it, they can change it in the theme editor.
Blank repo, no starter theme
I did not start from Dawn or from a paid theme. The design had its own layout, its own type and its own motion. To get there from a general theme, I would first have to remove that theme’s ideas from every section and then add ours. Writing the theme from zero was less work than that.
The theme lives in a GitHub repo and deploys through Shopify’s GitHub integration, from a branch called duwi/main. Every change is a commit, so there is a history of who changed what.
Everything is a section
Here is what the home page is made of:
- hero banners for the product
- a scrolling text strip that says “Science-led skincare”
- a logo strip
- image-with-text blocks
- testimonials
- a collection list
Each one is a Shopify section with its own settings. The founders can reorder them, change the text and swap the images.
If you have not written a section before, this is the whole idea. A section is one Liquid file with two parts: the markup, and a schema block that tells the theme editor which settings to show. Here is a cut-down example of the scrolling strip. It shows the pattern, it is not the exact file from the theme:
<div class="scrolling-strip" style="--speed: {{ section.settings.speed }}s">
{% for i in (1..6) %}
<span>{{ section.settings.text }}</span>
{% endfor %}
</div>
{% schema %}
{
"name": "Scrolling text",
"settings": [
{ "type": "text", "id": "text", "label": "Text", "default": "Science-led skincare" },
{ "type": "range", "id": "speed", "label": "Speed (seconds)", "min": 10, "max": 60, "step": 5, "default": 30 }
],
"presets": [{ "name": "Scrolling text" }]
}
{% endschema %}
Every entry in settings becomes a field in the theme editor. presets is the line people forget. Without it, the section exists in the code but does not appear in the “Add section” list, so the founders could never add a second one.
Two things are usually hard-coded in themes, and I made them sections too. One is the newsletter popover with the first-order discount. The other is the age check overlay. Both can be switched on or off, or reworded, from the theme editor.
Reviews come from Judge.me and the Instagram feed from Instafeed. These are apps, and I restyled both so they don’t look pasted in.
The difficult part of “editable”
Matching a Figma frame in code is one job. The harder job is that the section must still look right later, when someone puts in a longer headline or a photo with a different shape. A designer’s frame has perfect text and a perfect image. The theme editor will not.
So every section had to hold up at every breakpoint with whatever copy and images the founders put in later, not only with what was in the Figma file.
Three habits help with this, and they cost little:
- Give every setting a default. A section that was just added should look finished, not empty.
- Do not set fixed heights on text. A heading that fits in one line in Figma will be three lines one day. Let the box grow.
- Crop images with CSS, not by hoping.
object-fit: coverwith a set ratio means a tall photo and a wide photo both fill the same space.
The test came in November
Dūwi launched around one product, Hydration Lock, an oil for skin and hair. The home page had to sell that one product strongly. But I did not want a page that works only for one product.
This November they added a second product, Hydration Key, a ceramide moisturizer. It went onto the home page using the same banner and image-with-text sections as the oil. No new code was needed.
Why two months was enough
Design and development were one team. When I had a question about a Figma frame, I got the answer the same day. With an outside design studio, every such question becomes an email thread.