Changelog
Latest updates to achrafash.com.
- 2.1.0. Multi Service Architecture
- 2.0.3. Island status bar
- 2.0.1. Personal Changelog
- 2.0.0. 2.0 Redesign
- 2.1.0 — Released on Mar 28, 2026
Multi Service Architecture
Introducing a multi-service re-architecture to isolate each service (
salah,lab,blog) instead of using the_prefix hack. This keepssrc/pages/clean while preserving existing URLs.src/ ├── services/ │ ├── setup.ts # Generic integration setup │ └── salah/ │ ├── pages/ │ ├── components/ │ ├── migrations/ │ └── scripts/ ├── lab/ │ ├── setup.ts # Lab service integration │ ├── pages/ │ ├── prompted-dictation/ │ ├── structured-dictation/ │ └── ... ├── blog/ │ ├── setup.ts # Blog service integration │ ├── layout.astro │ └── pages/ ├── common/ # Shared across services └── pages/ # Root-level pages only (index, 404, etc.)- services: Auto-discovers services by finding
pages/directories - lab: Handles content collections + injects API routes for experiments
- blog: Handles blog post listing and individual post routes
- salah: Handles prayer time service
Behind the scenes
Each integration add pages from its directory:
export default function createLabIntegration(): AstroIntegration { return { name: "lab", hooks: { "astro:config:setup": ({ injectRoute }) => { injectLabRoutes(path.join(__dirname, "pages"), "/lab", injectRoute); injectApiRoutes(__dirname, "/lab", injectRoute, logger); } } }; } - services: Auto-discovers services by finding
- 2.0.3 — Released on Oct 19, 2025
Island status bar
I try to add Islands to most of my pages—embedded interactive components.
To keep design consistent, I needed a toast-like solution for notifying users. So inspired by Raycast I’m introducing the status bar!
Islands now feature a caption that can be controlled by the inner components to display: info, warning, error, or success messages.
Behind-the-scenes
Island components can use the
useStatusBarhook to set the status bar content—just like a regular toast library:import { useStatusBar } from "@/common/components/container"; export function Child() { const { set } = useStatusBar(); set({ message: "Loading…", level: "info" }); set({ message: "Heads up", level: "warn" }); set({ message: "Something failed", level: "error" }); set({ message: "Saved", level: "success" }); set(null); // clear; goes back to default caption }I’ve also added an error boundary in the
Containercomponent so exceptions are handled softly and an error message is displayed in the status bar.Caveat
Unfortunately, Astro seems to isolate components in MDX files so this doesn’t work:
// mdx file <Container> <IslandComponent /> </Container>I tried every client directive combinations, but I keep getting:
[ERROR] useStatusBar must be used inside <Container>So I instead have to put
ContainerinsideIslandComponentand use it as a single component. - 2.0.1 — Released on Oct 17, 2025
Personal Changelog
Introducing the Personal Changelog.
Kudos to these websites that inspired me:
Design inspired by Neon’s changelog.
Behind the scenes
The changelog shares the same infrastructure as the blog, that is:
- Astro’s content layer
- markdown files for each release in a
/releasesfolder - Tailwind’s typography plugin for consistent styling
- 2.0.0 — Released on Aug 16, 2025
2.0 Redesign
After supporting a growing number of concepts—blog, functional prototypes, mini apps—this website needed a big redesign.
And after watching my side projects inevitably die while my personal website stuck around, I reckoned it had to be my most polished personal work.Under the hood
- Content collections everywhere: using Astro’s content collections for posts, metatags, and releases makes authoring consistent and type‑safe.
// src/content.config.ts const blog = defineCollection({ loader: glob({ pattern: "*.(md|mdx)", base: "./posts" }), schema: z.object({ slug: z.string(), title: z.string(), date: z.string() }) }); // src/pages/blog/index.astro const posts = await getCollection("blog", (post) => { if (import.meta.env.PROD) { return !post.data.draft && !post.data.unlisted; } return true; });-
Modular folder structure: clearer separation between content, pages, and components to support posts, interactive islands, and mini apps.
-
Tailwind CSS v4: the new css-based configuration is awesome. I now have a single
base.cssfile. No extra config. Migration was smooth but the official v4 upgrade CLI would’ve saved me a few minutes if I had saw it earlier. -
Typography: while upgrading Tailwind I came across the plugin ecosystem so I had to try it. The Typography plugin has been amazing for styling layouts.
// src/pages/blog/_layout.astro <article class:list={[ // Globals "prose prose-gray prose-invert mx-auto max-w-lg", // Headings "prose-headings:font-serif prose-headings:mb-2...", // Links "prose-a:-mx-0.5 prose-a:rounded prose-a:bg-gray-500/20...", ... ]} > <slot /> </article>Polish
- Status badge on the home page.
- Interactive islands now feel like embedded widgets with a subtly contrasting background (see Arc bookmarks search).
- Apple‑inspired borders for floating elements (email form, islands, etc.).
- New typefaces: Space Grotesk (sans), Gloock (serif), Geist (display), Geist Mono (mono), Amiri (Arabic).
Misc
- Table of contents in blog posts (inspired by rauno.me).
- Semantic Versioning feels like I’m crafting this site, and helps me accept that it can’t be perfect on the first try. A version tag was added in the footer.