A warm, colorful theme for long evenings of code. Caramel keywords, burnt orange functions and a few sweet sprinkles, all on a calm milk chocolate tray.
/**
* A tiny bakery queue.
* @see https://github.com/gregadro/brownie-theme
*/
export enum Topping {
SaltedCaramel = "salted-caramel",
Walnut = "walnut",
None = "none",
}
export interface Brownie {
readonly id: number;
topping: Topping;
bakedAt?: Date;
}
const MAX_TRAY_SIZE = 16;
const slugPattern = /^[a-z]+(-[a-z]+)*$/g;
export class Oven<T extends Brownie = Brownie> {
private tray: T[] = [];
constructor(private readonly temperature: number = 180) {}
add(item: T): this {
if (this.tray.length >= MAX_TRAY_SIZE) {
throw new Error(`Tray is full (${MAX_TRAY_SIZE} pieces)\n`);
}
this.tray.push(item);
return this;
}
async bake(minutes = 25): Promise<T[]> {
await new Promise((resolve) => setTimeout(resolve, minutes * 1000));
const now = new Date();
return this.tray.map((b) => ({ ...b, bakedAt: now }));
}
}
const oven = new Oven(175);
const isSlug = slugPattern.test(Topping.SaltedCaramel);
oven
.add({ id: 1, topping: Topping.SaltedCaramel })
.add({ id: 2, topping: isSlug ? Topping.Walnut : Topping.None })
.bake()
.then((tray) => console.log(tray.length, "brownies ready", true, null));Every box has the same milk chocolate tray, cream text, caramel and burnt orange. Only the sprinkles change. Each one comes as a dark Brownie and a light Blondie.
/**
* A tiny bakery queue.
* @see https://github.com/gregadro/brownie-theme
*/
export enum Topping {
SaltedCaramel = "salted-caramel",
Walnut = "walnut",
None = "none",
}
export interface Brownie {
readonly id: number;
topping: Topping;
bakedAt?: Date;
}
const MAX_TRAY_SIZE = 16;
const slugPattern = /^[a-z]+(-[a-z]+)*$/g;
export class Oven<T extends Brownie = Brownie> {
private tray: T[] = [];
constructor(private readonly temperature: number = 180) {}
add(item: T): this {
if (this.tray.length >= MAX_TRAY_SIZE) {
throw new Error(`Tray is full (${MAX_TRAY_SIZE} pieces)\n`);
}
this.tray.push(item);
return this;
}
async bake(minutes = 25): Promise<T[]> {
await new Promise((resolve) => setTimeout(resolve, minutes * 1000));
const now = new Date();
return this.tray.map((b) => ({ ...b, bakedAt: now }));
}
}
const oven = new Oven(175);
const isSlug = slugPattern.test(Topping.SaltedCaramel);
oven
.add({ id: 1, topping: Topping.SaltedCaramel })
.add({ id: 2, topping: isSlug ? Topping.Walnut : Topping.None })
.bake()
.then((tray) => console.log(tray.length, "brownies ready", true, null));<template>
<h1 class="title">The Best Gooey Salted Caramel Brownies</h1>
<h3 class="credits">
From
<a href="https://tasty.co/recipe/the-best-gooey-salted-caramel-brownies">
Tasty
</a>
by {{ author }}
</h3>
<button v-if="isMember" @click="shareRecipe">Share</button>
<section class="recipe">
<div class="ingredients">
<h2 class="ingredients__title">Ingredients</h2>
<h4>
<i>(for {{ servings }} servings)</i>
</h4>
<ul>
<li v-for="(ingredient, idx) in ingredients" :key="`ingredient_${idx}`">
{{ ingredient }}
</li>
</ul>
</div>
<RecipeSteps :steps="steps" />
</section>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
import RecipeSteps from "./RecipeSteps.vue";
const props = defineProps<{ author: string; isMember?: boolean }>();
const servings = ref(16);
const ingredients = ref<string[]>([
"nonstick cooking spray, for greasing",
"12 tablespoons unsalted butter, 1 1/2 sticks, melted",
"1 cup granulated sugar (200 g)",
"1/2 cup brown sugar (110 g)",
// ... and more delicious stuff
]);
const steps = computed(() => [
`Preheat the oven to ${props.isMember ? 175 : 180}°C.`,
"Grease an 8x8-inch (20x20-cm) baking pan.",
]);
function shareRecipe(): void {
// Share this awesome recipe
navigator.clipboard.writeText(window.location.href);
}
</script>
<style scoped>
.title {
color: #d4a373;
}
</style>@import url("https://fonts.googleapis.com/css2?family=Lexend+Deca&display=swap");
/* Brownie palette */
:root {
--chocolate: #211a16;
--caramel: #d4a373;
--cream: #f1e4d3;
--radius: 0.75rem;
--gap: clamp(1rem, 2vw, 2rem);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: "Lexend Deca", system-ui, sans-serif;
background-color: var(--chocolate);
color: var(--cream);
}
.recipe {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--gap);
max-width: 960px;
margin: 0 auto;
padding: calc(var(--gap) * 2);
}
.recipe__title {
color: var(--caramel);
font-size: 2.5rem;
line-height: 1.1;
}
#salted-caramel a:hover,
#salted-caramel a:focus-visible {
color: rgb(242 160 78 / 80%);
text-decoration: underline dotted;
}
.ingredients li::marker {
content: "🍫 ";
}
.rating button[disabled] {
opacity: 0.5;
cursor: not-allowed !important;
}
@media (prefers-color-scheme: light) {
:root {
--chocolate: #fbf5ee;
--cream: #3b2a20;
}
}
@keyframes melt {
from {
transform: scaleY(1);
}
to {
transform: scaleY(0.9) translateY(4px);
}
}"""Bakery inventory, a small Python example for the Brownie theme."""
from __future__ import annotations
import re
from dataclasses import dataclass, field
from enum import Enum
from typing import Iterator
LOW_STOCK = 3
SKU_PATTERN = re.compile(r"^BRW-\d{4}$")
class Flavor(Enum):
SALTED_CARAMEL = "salted caramel"
WALNUT = "walnut"
DOUBLE_CHOCOLATE = "double chocolate"
@dataclass(frozen=True)
class Brownie:
sku: str
flavor: Flavor
price: float = 2.5
tags: tuple[str, ...] = field(default_factory=tuple)
def __post_init__(self) -> None:
if not SKU_PATTERN.match(self.sku):
raise ValueError(f"Invalid SKU: {self.sku!r}\n")
class Inventory:
def __init__(self) -> None:
self._stock: dict[str, int] = {}
def add(self, brownie: Brownie, quantity: int = 1) -> Inventory:
self._stock[brownie.sku] = self._stock.get(brownie.sku, 0) + quantity
return self
def low_stock(self) -> Iterator[str]:
yield from (sku for sku, qty in self._stock.items() if qty <= LOW_STOCK)
@property
def total(self) -> int:
return sum(self._stock.values())
if __name__ == "__main__":
caramel = Brownie("BRW-0001", Flavor.SALTED_CARAMEL, tags=("bestseller",))
walnut = Brownie("BRW-0002", Flavor.WALNUT, price=2.9)
inventory = Inventory().add(caramel, 12).add(walnut, 2)
# Only walnut is running low
print(list(inventory.low_stock()), inventory.total, None, True){
"name": "Salted Caramel Brownie",
"servings": 16,
"vegan": false,
"notes": null,
"oven": {
"temperature": 180,
"unit": "°C",
"rack": {
"position": "middle",
"pan": {
"size": "20x20 cm",
"lining": {
"type": "parchment paper",
"greased": true
}
}
}
},
"ingredients": [
{ "item": "butter", "amount": 170, "unit": "g" },
{ "item": "sugar", "amount": 200, "unit": "g" }
]
}# Salted Caramel Brownies
> The best brownie is the one you are eating right now.
## Ingredients
- **12 tablespoons** unsalted butter, _melted_
- 1 cup granulated sugar
- ~~raisins~~ (never)
- 1 teaspoon `salt`
## Steps
1. Preheat the oven to **180 °C**.
2. Mix everything, see [the recipe][tasty].
3. Bake for _25 minutes_.
```js
const brownie = bake({ minutes: 25 });
```
| Ingredient | Amount |
| ---------- | -----: |
| Butter | 170 g |
| Sugar | 200 g |
---
[tasty]: https://tasty.co/recipe/the-best-gooey-salted-caramel-browniesEvery color has a name and a job. Click a jar to copy its hex. All syntax colors reach at least 4.5:1 contrast on the tray, and the build refuses colors that look too alike.
I made Brownie in 2022 for my own Vue work. The idea was simple: writing code should feel as good as eating a salted caramel brownie. Warm, soft on the eyes, a little sweet, with a pinch of salt.
This year it got a proper re-bake. The colors are clearer and easier to tell apart, there is a light Blondie for sunny days, and two new flavors if you like your brownie with berries or cherries.
If it makes your editor a little cozier, that is all I wanted.
Grega
Search for Brownie in the Extensions view (Ctrl Shift X), or get it from the Marketplace.
Open the theme picker with Ctrl K Ctrl T (⌘ K ⌘ T on a Mac) and type Brownie or Blondie.
Brownie at night, Blondie by day. Add this to your settings.json:
{
"window.autoDetectColorScheme": true,
"workbench.preferredDarkColorTheme": "Brownie",
"workbench.preferredLightColorTheme": "Blondie"
}