Skip to content

Development notes

Adding bejamas/ui: start with one Button without handing over the site

Starting with a native Astro Button, this note records how we reviewed the CLI, styles, dependencies, and HTML semantics when adding UI components on demand, while preserving the existing design and limiting the scope of changes.

林予晴
Published
  • Astro
  • bejamas/ui
  • Tailwind CSS
  • Component design

This record covers the initial integration stage on October 1, 2026. The colors and Font Awesome setup below describe that stage, not the present site palette or icon system.

This portfolio site had just moved to native Astro. Its pages already had a gray-blue and bright-blue palette and their own button styles. We wanted shared UI components to reduce repetitive interface code on later pages, but installing a component was not meant to redesign the whole site.

We limited this first step to one Button. Before deciding whether to add more, we checked how the component entered the project, whether it could use the existing colors, and whether its HTML matched its intended use.

I wanted to see how many places a single component would change. If the site-wide background, text colors, and existing controls changed along with it, tracing the cause of a problem would become much harder.

First, understand how the component enters the project

We chose bejamas/ui, a component system for Astro and Tailwind CSS v4. Its official workflow uses a CLI to add components on demand.[4]

The Button source code is copied into our project, where we can edit it directly. This approach is often called copy-and-own. It also makes us responsible for understanding and maintaining that code; owning the source does not remove the need to review updates.

I first checked where the component would live, which helper functions and packages it needed, and how it would affect the global CSS. Controls such as Dialog and Tabs on the demo site could wait until a page actually needed them.

Verifying one native button does not verify the entire set of interactive components. If we later need a dialog or drawer, its keyboard behavior, focus handling, and additional browser scripts will need separate checks.

Run a dry run before changing site-wide settings

We checked the CLI documentation available at the time, pinned bejamas 0.4.1, and added an entry to package.json. This excerpt contains only the setting relevant to that command, not a complete installation file:

{
  "scripts": {
    "ui:add": "bejamas add"
  }
}

With that configuration and the CLI installed, we could run this preview command in the project:

npm run ui:add -- button --dry-run

--dry-run shows the content the command plans to write.[5] ui:add is a script name we defined ourselves, not a built-in npm or Astro command. Other projects need their own setup first; the official documentation at the end covers installation.

After adding the component, we still reviewed the actual changes one by one. Alongside Button, the command added a helper for merging classes, style imports, and dependencies. Naming just one component can still affect several files.

Pinning the CLI version is no reason to skip review. Our rule was to inspect what each add operation actually generated, avoid blindly running the full init or apply workflow, and never overwrite components we had already customized.

Keep colors in their existing source

Adding Button did not change the existing gray-blue and bright-blue palette. A color change could be a separate round of work rather than part of component integration.

The component uses names such as background, foreground, border, and ring for backgrounds, text, borders, and focus indicators. We mapped these semantic colors to the existing palette. This is a short excerpt from the actual styles:

@theme {
  --color-siteBg: #e0e6ec;
  --color-black: #1c1e19;
  --color-primary: #273cf7;
}

@theme inline {
  --color-background: var(--color-siteBg);
  --color-foreground: var(--color-black);
  --color-ring: var(--color-primary);
}

@theme preserves the site’s original palette; @theme inline maps the names the component needs to those colors. These shared settings are design tokens. Keeping them together means each page does not need its own set of color codes.

We also kept the existing Font Awesome icon system. A different icon set in the component examples was not a reason to replace icons across the site.

The style review included the global base rules added by the CLI. This CSS can affect elements beyond the button, and we did not keep all of it. In particular, the site’s existing focus-visible indicators needed to remain visible when navigating with the Tab key.

The trade-off was practical: adapt the component to the site first, then adjust the design tokens together when a real redesign calls for it.

A button-like appearance still needs the right element

Controls can look alike while serving different purposes. Navigation to another page should produce an <a> with a real href; an action should use a <button>. This minimal example omits styling and action scripts. The original Traditional Chinese labels and route example are retained: the labels mean “About this site,” “Open menu,” and “Submit form,” respectively.

<a href="/about">關於這個網站</a>
<button type="button">開啟選單</button>
<button type="submit">送出表單</button>

The Button component we added supports as="a", so the first use still produces a real link. Readers can open it in a new tab using the browser’s normal behavior, without turning every navigation action into a JavaScript click event.

We also made action buttons default to type="button", passing type="submit" explicitly when a form needs to be submitted. That was a local change in this project, not a claim about the component library’s defaults in every version.

These differences are not necessarily visible, so verification cannot stop at “the button has the right color.” We also need to read the actual HTML and check the tag, attributes, and class-merging result.

Test components that have no usage site yet

During the initial integration, Button had not replaced any controls on the existing pages. A successful site build therefore did not prove the new component had actually been rendered.

We made a test-only page that imported the same Button and CSS and rendered its different sizes, styles, and link usage. We then inspected the generated HTML: whether links had an href, action buttons had the correct type, disabled states and merged classes matched expectations, and any extra browser scripts were present.

That record demonstrates that Button worked within the scope of that test. It does not mean every interactive component, mobile layout, or redesigned site had been accepted.

The integration also ran into a locked native-module file on Windows. npm ci failed at the time; we later restored the dependencies with npm install and completed the checks. That records how this particular incident was handled. It does not make npm install a universal fix for installation errors, nor does it prove that reinstalling from the lockfile was verified.

If the only record were “everything succeeded,” the next person working in a different environment would not know which conditions remained unverified.

Start the next component with a need, too

Starting with Button let us examine the component source, token mappings, the CLI’s write boundaries, and our local changes.

The same review approach can apply to Card or Badge later. More complex interactive controls need additional behavior checks. Using a component library does not require wrapping every HTML element in a component.

I want shared components to reduce repeated implementation, not our understanding of the output and behavior. The site’s information architecture, content, and visual choices still need to follow actual requirements.

Scope of this record

This note describes the stage when we added only the basic Button. The existing pages had not yet switched broadly to the new component. The command configuration and CSS excerpts come from that implementation; the HTML block is a minimal illustration of intended uses. Other interactive components and the later site-wide redesign are outside the scope of this verification.

Sources

[4] https://ui.bejamas.com/docs/installation [5] https://ui.bejamas.com/docs/cli