<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/feed.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:fh="http://purl.org/syndication/history/1.0" xml:lang="en">
  <id>https://normco.re/atom.xml</id>
  <title>PhiPhi’s Bizarre Aventure</title>
  <link rel="self" type="application/atom+xml" href="https://normco.re/atom.xml"/>
  <link rel="alternate" type="text/html" href="https://normco.re/"/>
  <fh:complete/>
  <updated>2026-04-11T07:58:46.345Z</updated>
  <subtitle>Personal blog by Phiphi, based in Chengdu, China.</subtitle>
  <author>
    <name>Phiphi</name>
  </author>
  <entry>
    <id>https://normco.re/posts/alibaba-cloud-oss-cdn-deployment/</id>
    <title>How This Blog Deploys to Alibaba Cloud OSS and CDN</title>
    <link rel="alternate" type="text/html" href="https://normco.re/posts/alibaba-cloud-oss-cdn-deployment/"/>
    <updated>2026-03-10T00:00:00Z</updated>
    <published>2026-03-10T00:00:00Z</published>
    <summary>A practical walkthrough of my GitHub Actions pipeline: warmed Lume builds, OIDC role assumption, OSS sync, CDN refresh, and automatic cleanup.</summary>
    <content type="html">&lt;p&gt;This site is built with Lume and deployed to Alibaba Cloud OSS, with Alibaba
Cloud CDN in front of it. The deployment pipeline stays intentionally small: one
GitHub workflow, Deno available on the runner, and one custom action that
restores a local build cache, runs the build, syncs OSS, and cleans up:
&lt;a href=&quot;https://github.com/frenchvandal/aliyun-oss-cdn-sync-action&quot;&gt;&lt;code&gt;frenchvandal/aliyun-oss-cdn-sync-action&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I now want five properties at the same time: short-lived credentials, warmed
builds on fresh runners, predictable uploads, cache headers that match the files
being shipped, and automatic cache coherence. This setup gives me all five
without adding repository-specific deploy scripts.&lt;/p&gt;
&lt;h2&gt;Pipeline at a glance&lt;/h2&gt;
&lt;p&gt;In steady-state production, this repository’s workflow now does three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Checks out the repository.&lt;/li&gt;
&lt;li&gt;Installs Deno with the pinned version from &lt;code&gt;.tool-versions&lt;/code&gt; and enables the
runner-side tool cache.&lt;/li&gt;
&lt;li&gt;Runs the OSS/CDN sync action, which restores &lt;code&gt;_cache&lt;/code&gt;, executes
&lt;code&gt;deno task build&lt;/code&gt;, uploads &lt;code&gt;_site&lt;/code&gt;, refreshes CDN, and cleans up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At the moment, that build also fingerprints the shared and route-scoped critical
CSS assets, strips source maps, and prunes optional Pagefind files that are not
referenced by the final HTML before &lt;code&gt;_site&lt;/code&gt; is synced.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;name: Deploy static content to OSS

on:
  push:
    branches: [&amp;quot;master&amp;quot;]
  workflow_dispatch:

permissions:
  contents: read
  id-token: write

concurrency:
  group: &amp;quot;oss-deploy&amp;quot;
  cancel-in-progress: true

jobs:
  deploy:
    environment:
      name: development
    runs-on: macos-26
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup Deno environment
        uses: denoland/setup-deno@v2
        with:
          deno-version-file: .tool-versions
          cache: true

      - name: Deploy to Alibaba Cloud OSS
        uses: frenchvandal/aliyun-oss-cdn-sync-action@master
        with:
          role-oidc-arn: ${{ secrets.ALIBABA_CLOUD_ROLE_ARN }}
          oidc-provider-arn: ${{ secrets.ALIBABA_CLOUD_OIDC_PROVIDER_ARN }}
          role-session-name: ${{ github.run_id }}
          role-session-expiration: 3600
          cache-enabled: true
          cache-key: &amp;gt;-
            lume-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles(&apos;deno.lock&apos;) }}-${{ hashFiles(&apos;_cache/**/*&apos;) || &apos;empty&apos; }}
          cache-restore-keys: |
            lume-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles(&apos;deno.lock&apos;) }}-
            lume-cache-${{ runner.os }}-${{ runner.arch }}-
          input-dir: _site
          bucket: ${{ secrets.OSS_BUCKET }}
          region: ${{ secrets.OSS_REGION }}
          audience: ${{ github.repository_id }}
          cdn-enabled: true
          cdn-base-url: ${{ secrets.OSS_CDN_BASE_URL }}
          cdn-actions: refresh
          build-command: deno task build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this repository, I currently keep &lt;code&gt;cdn-actions: refresh&lt;/code&gt;. The action still
supports &lt;code&gt;refresh,preload&lt;/code&gt;, but this site’s workflow no longer enables preload
on every deployment.&lt;/p&gt;
&lt;h2&gt;Why OIDC instead of access keys&lt;/h2&gt;
&lt;p&gt;The action uses GitHub OIDC to assume an Alibaba Cloud RAM role at runtime. No
long-lived access key is stored in the repository. The workflow only needs
&lt;code&gt;id-token: write&lt;/code&gt; plus the RAM role ARN and the OIDC provider ARN.&lt;/p&gt;
&lt;p&gt;Authentication is now OIDC-only. The action does not fall back to static access
keys from inputs or environment variables if role assumption fails.&lt;/p&gt;
&lt;p&gt;The current setup also passes &lt;code&gt;audience: ${{ github.repository_id }}&lt;/code&gt;. GitHub
can mint an ID token for a custom audience, and Alibaba Cloud RAM can validate
that &lt;code&gt;aud&lt;/code&gt; value against the Client IDs configured on the OIDC identity provider
and in the role trust policy. That gives me a tighter trust boundary than
relying on default audience handling alone.&lt;/p&gt;
&lt;h2&gt;How the action runs internally&lt;/h2&gt;
&lt;p&gt;The action is split into three phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pre&lt;/strong&gt;: assumes the RAM role through OIDC and stores temporary credentials in
action state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;main&lt;/strong&gt;: optionally restores the local &lt;code&gt;_cache&lt;/code&gt; directory, runs
&lt;code&gt;build-command&lt;/code&gt;, uploads local files to OSS, applies cache headers, and runs
CDN actions if enabled.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;post&lt;/strong&gt;: compares remote objects under the destination prefix with local
files produced by the build, deletes remote orphans, refreshes deleted URLs
when needed, writes CDN task summaries, and saves &lt;code&gt;_cache&lt;/code&gt; when &lt;code&gt;cache-key&lt;/code&gt; is
configured.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The cleanup phase runs with &lt;code&gt;post-if: always()&lt;/code&gt;, so it still executes even when
earlier steps fail. Cleanup, CDN calls, and cache restore or save are
intentionally best-effort: warnings are logged, but transient CDN or cache
service issues do not block the deployment.&lt;/p&gt;
&lt;h2&gt;Upload, cache, quota, and drift control&lt;/h2&gt;
&lt;p&gt;A few implementation details matter for reliability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;build-command&lt;/code&gt; runs inside the action before any OSS upload or CDN call.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;cache-enabled&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;cache-key&lt;/code&gt; is configured, &lt;code&gt;_cache&lt;/code&gt; restore
happens before the build and a fresh snapshot can be saved during &lt;code&gt;post&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cache-enabled&lt;/code&gt; controls only restore, so I can skip warm-cache restore
temporarily without giving up the post-step cache save.&lt;/li&gt;
&lt;li&gt;Uploads are parallelized with &lt;code&gt;max-concurrency&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A global API throttle is applied through &lt;code&gt;api-rps-limit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each file upload is retried up to 3 times before being marked as failed.&lt;/li&gt;
&lt;li&gt;Partial upload failures are surfaced through &lt;code&gt;failed-count&lt;/code&gt; and the GitHub job
summary instead of being buried in the logs.&lt;/li&gt;
&lt;li&gt;The upload step now writes &lt;code&gt;Cache-Control&lt;/code&gt; headers automatically: HTML and
&lt;code&gt;sw.js&lt;/code&gt; are revalidated aggressively, hashed assets are uploaded as immutable,
and common static assets get shorter revalidation windows instead of a
one-size-fits-all policy.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cdn-actions&lt;/code&gt; only accepts &lt;code&gt;refresh&lt;/code&gt; or &lt;code&gt;refresh,preload&lt;/code&gt;; if CDN is enabled
and the value is empty or invalid, the action falls back to &lt;code&gt;refresh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The action checks remaining CDN quota before submitting refresh or preload
batches.&lt;/li&gt;
&lt;li&gt;Deleted OSS objects can trigger CDN refresh for removed URLs, which reduces
stale-cache windows and limits object drift over time.&lt;/li&gt;
&lt;li&gt;The action writes a GitHub Actions job summary for deployment, cleanup, and
CDN task status.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point is easy to miss: upload alone is not enough for a static site
that changes over time. You also need deletion and cache invalidation for
objects that should no longer exist.&lt;/p&gt;
&lt;h2&gt;Minimum RAM permissions&lt;/h2&gt;
&lt;p&gt;At the policy level, the role needs OSS permissions on the target bucket scope
for listing, uploading, and deleting objects. On the CDN side, this workflow
needs refresh permissions plus the read APIs used for quota checks and task
lookups. If preload is enabled, add the corresponding preload permission too.
The trust policy must allow the GitHub OIDC provider to assume the deploy role.&lt;/p&gt;
&lt;p&gt;I keep this as a dedicated deploy role rather than mixing it with broader
operational permissions.&lt;/p&gt;
&lt;h2&gt;Reusing the action in other repositories&lt;/h2&gt;
&lt;p&gt;The action is published and reusable:
&lt;a href=&quot;https://github.com/frenchvandal/aliyun-oss-cdn-sync-action&quot;&gt;github.com/frenchvandal/aliyun-oss-cdn-sync-action&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This repository currently tracks &lt;code&gt;@master&lt;/code&gt; because I maintain the site and the
action together. In a separate repository, I would usually pin &lt;code&gt;@v1&lt;/code&gt; or, better
yet, a full commit SHA. I also now let the action own the build and &lt;code&gt;_cache&lt;/code&gt;
lifecycle, which keeps consumer workflows shorter and more declarative.&lt;/p&gt;
&lt;p&gt;For me, the key result is that deploys stay boring: restore cache, build, sync,
refresh, cleanup, done.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://normco.re/posts/lorem-ipsum/</id>
    <title>Lorem Ipsum and the Art of Placeholder Text</title>
    <link rel="alternate" type="text/html" href="https://normco.re/posts/lorem-ipsum/"/>
    <updated>2026-02-18T00:00:00Z</updated>
    <published>2026-02-18T00:00:00Z</published>
    <summary>A meditation on placeholder text, its history, and why it still matters in modern design workflows.</summary>
    <content type="html">&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.&lt;/p&gt;
&lt;p&gt;Most people know the phrase but few know its origin. It is a scrambled excerpt
from Cicero’s &lt;em&gt;de Finibus Bonorum et Malorum&lt;/em&gt;, a philosophical treatise written
in 45 BC. The text has been used as filler copy since the 1500s, when an unknown
printer took a galley of type and scrambled it to make a type specimen book.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://normco.re/posts/lorem-ipsum/images/amol-srivastava-uOYc6OlgpUI-unsplash.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Why placeholder text matters&lt;/h2&gt;
&lt;p&gt;Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.&lt;/p&gt;
&lt;p&gt;When you design a layout with real words, you design for those specific words.
Placeholder text forces you to design for the structure, not the content. This
is a valuable constraint—it reveals whether your design can accommodate the
unexpected: a headline that runs to three lines, a paragraph with no natural
break, a word too long for its container.&lt;/p&gt;
&lt;h2&gt;A question of fidelity&lt;/h2&gt;
&lt;p&gt;Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit
laboriosam? There is a tension in design between fidelity to real content and
the freedom that abstraction permits.&lt;/p&gt;
&lt;p&gt;High-fidelity prototypes with real copy require real copy to exist first.
Low-fidelity wireframes communicate structure without the distraction of
meaning. Both approaches have their place. The key is knowing which tool serves
the moment.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// A small utility to generate repeating text blocks.
function lorem(words: number): string {
  const base = &amp;quot;lorem ipsum dolor sit amet consectetur adipiscing elit&amp;quot;;
  const tokens = base.split(&amp;quot; &amp;quot;);
  const result: string[] = [];
  for (let i = 0; i &amp;lt; words; i++) {
    result.push(tokens[i % tokens.length] ?? &amp;quot;&amp;quot;);
  }
  return result.join(&amp;quot; &amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://normco.re/posts/lorem-ipsum/images/scott-rodgerson-z0MDyylvY1k-unsplash.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Lorem ipsum is more than filler. It is a mirror held up to the design—one that
reflects structure stripped of meaning, form without content. That is precisely
why it endures.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://normco.re/posts/vestibulum-ante/</id>
    <title>Vestibulum Ante: On Thresholds and New Beginnings</title>
    <link rel="alternate" type="text/html" href="https://normco.re/posts/vestibulum-ante/"/>
    <updated>2025-09-04T00:00:00Z</updated>
    <published>2025-09-04T00:00:00Z</published>
    <summary>Reflections on transitions, the in-between spaces of life, and what it means to stand at a threshold.</summary>
    <content type="html">&lt;p&gt;Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia
curae. The Latin word &lt;em&gt;vestibulum&lt;/em&gt;—a forecourt, an entrance hall, the space
before the door—carries more weight than its English descendant “vestibule”
suggests.&lt;/p&gt;
&lt;p&gt;A threshold is not an end, nor a beginning. It is the liminal space between the
two: the breath held before the first word, the pause after a key turns in a
lock. It is where one thing stops and another has not yet started.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://normco.re/posts/vestibulum-ante/images/bruno-martins-4cwf-iW6I1Q-unsplash.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;The value of the in-between&lt;/h2&gt;
&lt;p&gt;Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac
turpis egestas. We live in a culture that devalues transition. We want results,
outcomes, the finished thing. The messy middle—the drafts, the doubt, the slow
accumulation of understanding—goes undocumented and uncelebrated.&lt;/p&gt;
&lt;p&gt;But the in-between is where most of life happens. Commutes. Waiting rooms. The
gap between sending a message and reading the reply. The years between knowing
what you want and knowing how to get there.&lt;/p&gt;
&lt;h2&gt;Standing at the door&lt;/h2&gt;
&lt;p&gt;Fusce suscipit varius mi. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Phasellus viverra nulla ut metus varius laoreet.&lt;/p&gt;
&lt;p&gt;I moved to Chengdu on a Tuesday in early autumn. The city was warm in a way that
surprised me—not the heat of summer, but the warmth of a place that had already
decided it liked you. The streets smelled of chilli oil and osmanthus, and
everything felt slightly, pleasantly unfamiliar.&lt;/p&gt;
&lt;p&gt;That is what a threshold feels like, I think. Not hostile. Not welcoming. Simply
open—waiting to see what you will make of it.&lt;/p&gt;
&lt;h2&gt;Coda&lt;/h2&gt;
&lt;p&gt;Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi,
condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit
eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim
ac dui.&lt;/p&gt;
&lt;p&gt;Every document has a vestibulum—the title page, the introduction, the first
paragraph. It is the space that prepares the reader for what follows. Write it
with care.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://normco.re/posts/proin-facilisis/</id>
    <title>Proin Facilisis: Making Things Easier</title>
    <link rel="alternate" type="text/html" href="https://normco.re/posts/proin-facilisis/"/>
    <updated>2025-04-12T00:00:00Z</updated>
    <published>2025-04-12T00:00:00Z</published>
    <summary>On the philosophy of reducing friction—in code, in design, and in everyday life.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;Proin facilisis&lt;/em&gt;—Latin for “promoting ease.” It appears in old botanical texts
to describe a plant that aids digestion, smooths a passage, clears an
obstruction. As a philosophy for building software, it translates remarkably
well.&lt;/p&gt;
&lt;p&gt;Good software reduces friction. It anticipates the user’s next step. It provides
the right affordance at the right moment. It gets out of the way.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://normco.re/posts/proin-facilisis/images/brett-jordan-92-mTYj5oGs-unsplash.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;The friction inventory&lt;/h2&gt;
&lt;p&gt;Proin in tellus sit amet nibh dignissim sagittis. The first step in reducing
friction is to map it. Where does the user slow down? Where does attention
spike? Where do errors cluster?&lt;/p&gt;
&lt;p&gt;In a typical web application, the highest-friction moments are predictable:
onboarding, form submission, error recovery, and loading states. These are the
vestibules of the product—the thresholds users must cross to reach the value
inside.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// Friction shows up in code too.
// Compare these two approaches to handling a missing value:

// High friction—the caller must always check:
function getUser(id: string): User | undefined {/* … */}

// Lower friction—the error is explicit and handled at the boundary:
function getUser(id: string): User {
  const user = db.find(id);
  if (user === undefined) {
    throw new Error(`User not found: ${id}`);
  }
  return user;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The paradox of ease&lt;/h2&gt;
&lt;p&gt;Vivamus pretium aliquet erat. There is a paradox at the heart of “making things
easy”: it is very hard to do. Eliminating friction requires deep understanding
of the user, the context, and the failure modes. It demands more work from the
builder so that less work falls on the user.&lt;/p&gt;
&lt;p&gt;This is why ease is a form of generosity. Every unnecessary step you remove from
your user’s path is time returned to them—time they can spend on the thing that
actually matters.&lt;/p&gt;
&lt;h2&gt;Facilisis in practice&lt;/h2&gt;
&lt;p&gt;Donec aliquet metus ut erat semper, et tincidunt nulla luctus. Some principles I
return to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Defaults should be correct for most users, most of the time.&lt;/li&gt;
&lt;li&gt;Error messages should explain what went wrong and how to fix it.&lt;/li&gt;
&lt;li&gt;The happy path should require no thought.&lt;/li&gt;
&lt;li&gt;Configuration should be possible, never required.&lt;/li&gt;
&lt;li&gt;Documentation is part of the product.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nulla facilisi. Phasellus blandit leo ut odio. Nam sed nulla non diam tincidunt
tempus. The name of this principle—&lt;em&gt;nulla facilisi&lt;/em&gt;, “no easy thing”—is a
reminder that ease, properly understood, is never accidental.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://normco.re/posts/instructions/</id>
    <title>How to install this theme?</title>
    <link rel="alternate" type="text/html" href="https://normco.re/posts/instructions/"/>
    <updated>2022-06-12T00:00:00Z</updated>
    <published>2022-06-12T00:00:00Z</published>
    <summary>A quick guide to setting up the Simple Blog theme for Lume.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Simple blog&lt;/strong&gt; is a clean and minimal blog theme for Lume, with support for
tags and authors. It allows you to build your own blog &lt;strong&gt;in seconds&lt;/strong&gt;, and
provides Atom and JSON feeds for your subscribers.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;fastest and easiest&lt;/strong&gt; way to configure this theme is the
&lt;a href=&quot;https://deno.land/x/lume_init&quot;&gt;Lume init command&lt;/a&gt;, which you can also copy
easily from the &lt;a href=&quot;https://lume.land/theme/simple-blog/&quot;&gt;Simple Blog theme page&lt;/a&gt;.
Running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;deno run -A https://lume.land/init.ts --theme=simple-blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will create a new project with Simple Blog configured. Edit the &lt;code&gt;_data.yml&lt;/code&gt; file
in your blog root folder with your data to customize the site title,
description, and metadata.&lt;/p&gt;
&lt;p&gt;Posts must be saved in the &lt;code&gt;posts&lt;/code&gt; folder. For example,
&lt;code&gt;posts/my-first-post.md&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Install as a remote theme&lt;/h2&gt;
&lt;p&gt;To add the theme to an existing Lume project, import it in your &lt;code&gt;_config.ts&lt;/code&gt;
file as a remote module. Update it by changing the version number in the import
URL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import lume from &amp;quot;lume/mod.ts&amp;quot;;
import blog from &amp;quot;https://deno.land/x/lume_theme_simple_blog@v0.15.6/mod.ts&amp;quot;;

const site = lume();

site.use(blog());

export default site;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy the
&lt;a href=&quot;https://github.com/lumeland/theme-simple-blog/blob/main/src/_data.yml&quot;&gt;&lt;code&gt;_data.yml&lt;/code&gt;&lt;/a&gt;
file to your blog root folder and edit it with your data.&lt;/p&gt;
&lt;h2&gt;Customization&lt;/h2&gt;
&lt;p&gt;You can use &lt;a href=&quot;https://lume.land/cms&quot;&gt;lumeCMS&lt;/a&gt; to customize the blog and add
content easily.&lt;/p&gt;
</content>
  </entry>
</feed>
