bascik

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bascik

Bascik

Bascik is a build tool for HTML components. It expands custom tags such as
<site-nav />
into the matching component file, scopes the component's CSS, IDs, classes, and scripts, and writes plain HTML pages to
dist/
. It adds zero JavaScript: every script in the output is one you wrote.
It is not a framework. There is no virtual DOM, reactive state, client router, template language, Shadow DOM, or runtime. Write HTML, CSS, and TypeScript (stripped to plain JavaScript), and let Bascik do the isolation at build time.
Bascik 是一个 HTML 组件构建工具。它会将
<site-nav />
等自定义标签展开为匹配的组件文件,对组件的 CSS、ID、class 和 script 进行作用域隔离,并将普通 HTML 页面写入
dist/
。它不会添加任何 JavaScript:输出中的每个 script 都是你自己编写的。
它不是框架。没有虚拟 DOM、响应式状态、客户端 router、模板语言、Shadow DOM 或 runtime。编写 HTML、CSS 和 TypeScript(剥离为纯 JavaScript),让 Bascik 在构建时完成隔离。

Pages read like a list of tags

页面就像一组标签列表

Write pages as a list of component tags, not big blocks of HTML:
html
<body>
  <site-header />
  <main id="main">
    <home-hero />
    <pricing-section>
      <plan-card data-bascik-prop-name="Home" data-bascik-prop-price="$49" />
      <plan-card data-bascik-prop-name="Cafe" data-bascik-prop-price="$129" />
    </pricing-section>
    <faq-list />
  </main>
  <site-footer />
</body>
  • Split a section into a component when it has a job you can name (pricing, reviews, signup form), when its markup repeats, or when the page is no longer readable at a glance.
  • Do not split a piece that only makes sense inside its parent, that would need a prop passed through a component that does not use it, or that is a tiny wrapper used once. Use a slot, or keep the parent whole.
  • Write each fact once. An address, opening hours, a price, or a phone number lives in one component or one data file, never typed into several. A fact several components show goes in
    src/lib/site.ts
    ; data only one component renders (its FAQ entries, its plans) lives in that component's folder (rule 17). A value that is the same on every page, such as the brand name, belongs inside the component, not passed as a prop on every page. A heading group (eyebrow, heading, intro) that repeats across sections is a
    <section-head>
    component with props.
Small named files are easier for people and agents to read and change, and each is a boundary for its CSS, IDs, and script. It is a balance: the
bascik-components
skill has the detailed guidance with good and bad examples, and
scripts/audit-components.mjs
checks it.
像编写组件标签列表一样编写页面,而不是堆砌大段 HTML:
html
<body>
  <site-nav />
  <main id="main">
    <home-hero />
    <pricing-section>
      <plan-card data-bascik-prop-name="Home" data-bascik-prop-price="$49" />
      <plan-card data-bascik-prop-name="Cafe" data-bascik-prop-price="$129" />
    </pricing-section>
    <faq-list />
  </main>
  <site-footer />
</body>
  • 当某个区块承担了可以明确命名的职责(pricing、reviews、signup form)、markup 出现重复,或者页面已经无法一眼读懂时,将其拆分为组件。
  • 如果某部分只有放在父组件中才有意义、需要把 prop 传给一个根本不会使用它的组件,或者只是仅使用一次的小型 wrapper,就不要拆分。使用 slot,或者保留完整的父组件。
  • 每个事实只编写一次。 地址、营业时间、价格或电话号码应只存在于一个组件或一个数据文件中,绝不能重复输入。一个事实需要由多个组件展示时,将它放在
    src/lib/site.ts
    ;只由一个组件渲染的数据(FAQ 条目、套餐等)应放在该组件的文件夹中(规则 17)。每个页面都相同的值(例如品牌名称)应放在组件内部,而不是在每个页面上都作为 prop 传递。重复出现的标题组(eyebrow、heading、intro)应做成带 props 的
    <section-head>
    组件。
小型且命名清晰的文件更便于开发者和 Agent 阅读与修改,每个文件也是其 CSS、ID 和 script 的隔离边界。这需要权衡:
bascik-components
skill 提供了包含优秀与糟糕示例的详细指导,
scripts/audit-components.mjs
会对此进行检查。

TypeScript by default

默认使用 TypeScript

Write all new code in TypeScript. Use JavaScript only when the person asks for it. When refactoring TypeScript, keep it TypeScript and carry the types across.
  • Browser code goes in a component's companion
    name.ts
    (
    <script src="name.ts">
    ) or an inline
    <script type="text/typescript">
    . Bascik strips the types with Node's strip-only mode, so it works with no setup. Use erasable syntax only: annotations, interfaces,
    as
    ,
    !
    , and
    import type
    .
    enum
    , namespaces with code, and parameter properties fail the build. A plain
    <script>
    is JavaScript. A page's own
    <script src="x.ts">
    is compiled and inlined too; a classic one with
    defer
    or
    async
    fails the build (inline scripts ignore them), so drop the attribute or mark it
    type="module"
    . A
    type="module"
    .ts
    script is inlined, not bundled: importing
    ./util.ts
    (or any relative,
    @/
    or
    .ts
    path) fails the build, so bundle it with esbuild or import a published
    /js/*.js
    URL.
  • Build, routes, server, and stream scripts are TypeScript with a
    .ts
    src=
    or a tag marked
    type="text/typescript"
    (
    <script data-bascik-server type="text/typescript">
    ). Bascik strips them the same way before they run, and compiles them when the page builds, so a syntax error or
    enum
    fails the build with file:line:column; an unmarked block is JavaScript.
  • type="text/typescript"
    blocks are type checked
    in the editor (the VS Code extension or
    @bascik/language-server
    , with hover, definition and completion for imports) and, opted in, by
    bascik --check --typescript
    .
  • Node code is
    .ts
    too: helpers and data modules,
    src/api/
    routes,
    pipeline.exec
    scripts, and
    bascik.config.ts
    . Node strips their types. Keep each
    data-bascik-*
    block to a few lines that import typed helpers:
    ./faqs.ts
    for a module in the component's own folder,
    @/lib/posts.ts
    for one several pages share (rule 17). A helper that returns markup builds it with
    html
    from
    @bascik/bascik/html
    and declares
    SafeHtml
    as its return type (rule 15).
  • esbuild is the option for full TypeScript (
    enum
    , decorators, an older browser target):
    npm install -D esbuild
    , then set
    scripts.typescript
    to an esbuild transform. Browser code that imports npm packages needs esbuild too, as a
    pipeline.exec
    step that bundles it into
    dist/
    .
ts
// bascik.config.ts (illustrative: esbuild as the browser TypeScript compiler)
import { defineConfig } from "@bascik/bascik/config";
import { transform } from "esbuild";

export default defineConfig({
  scripts: {
    typescript: async (code, { sourcePath }) => {
      const result = await transform(code, {
        loader: "ts",
        target: "es2020",
        sourcefile: sourcePath,
      });
      return result.code;
    },
  },
});
Read
/scoped-javascript#typescript-in-component-scripts
,
/configuration#scriptstypescript
, and
/how-to/bundling-npm-packages
before going beyond erasable syntax.
Code in this skill is illustrative. Adapt tag names, paths, and values to the project instead of copying example names literally.
所有新代码都使用 TypeScript。只有在用户明确要求时才使用 JavaScript。重构 TypeScript 时,继续保留 TypeScript,并让类型贯穿整个重构。
  • 浏览器代码应放在组件的配套文件
    name.ts
    (
    <script src="name.ts">
    )或内联
    <script type="text/typescript">
    中。Bascik 使用 Node 的仅剥离模式移除类型,因此无需任何配置即可工作。仅使用可擦除语法:类型标注、interface、
    as
    、
    !
    和
    import type
    。
    enum
    、包含代码的 namespace 和参数属性会导致构建失败。普通
    <script>
    是 JavaScript。页面自身的
    <script src="x.ts">
    也会被编译并内联;带有
    defer
    或
    async
    的传统 script 会导致构建失败(内联 script 会忽略它们),因此应移除该属性或标记为
    type="module"
    。
    type="module"
    的
    .ts
    script 会被内联而不会被打包:导入
    ./util.ts
    (或任何相对路径、
    @/
    或
    .ts
    路径)都会导致构建失败,因此应使用 esbuild 打包,或导入已发布的
    /js/*.js
    URL。
  • 构建、路由、server 和 stream script 应使用带
    .ts
    的
    src=
    或标记为
    type="text/typescript"
    的标签(
    <script data-bascik-server type="text/typescript">
    )。Bascik 会以相同方式剥离类型,并在页面构建时编译它们,因此语法错误或
    enum
    会以 file:line:column 的形式导致构建失败;未标记的代码块是 JavaScript。
  • type="text/typescript"
    代码块
    会在编辑器中进行类型检查(VS Code 扩展或
    @bascik/language-server
    ,支持悬停、定义和导入补全),也可以选择通过
    bascik --check --typescript
    进行检查。
  • Node 代码也使用
    .ts
    :helper、数据模块、
    src/api/
    路由、
    pipeline.exec
    script 和
    bascik.config.ts
    。Node 会剥离它们的类型。让每个
    data-bascik-*
    代码块保持简短,并导入带类型的 helper:组件自己文件夹中的模块使用
    ./faqs.ts
    ,多个页面共享的模块使用
    @/lib/posts.ts
    (规则 17)。返回 markup 的 helper 应使用
    @bascik/bascik/html
    中的
    html
    构造 markup,并将返回类型声明为
    SafeHtml
    (规则 15)。
  • 完整 TypeScript 功能可使用 esbuild(
    enum
    、decorator、较旧的浏览器 target):运行
    npm install -D esbuild
    ,然后将
    scripts.typescript
    设置为 esbuild transform。导入 npm 包的浏览器代码也需要 esbuild,作为
    pipeline.exec
    步骤打包到
    dist/
    中。
ts
// bascik.config.ts (illustrative: esbuild as the browser TypeScript compiler)
import { defineConfig } from "@bascik/bascik/config";
import { transform } from "esbuild";

export default defineConfig({
  scripts: {
    typescript: async (code, { sourcePath }) => {
      const result = await transform(code, {
        loader: "ts",
        target: "es2020",
        sourcefile: sourcePath,
      });
      return result.code;
    },
  },
});
在超出可擦除语法范围之前,请阅读
/scoped-javascript#typescript-in-component-scripts
、
/configuration#scriptstypescript
和
/how-to/bundling-npm-packages
。
本 skill 中的代码仅作示例。应根据项目调整标签名称、路径和值,不要逐字照抄示例名称。

Recognize a Bascik project

识别 Bascik 项目

  • package.json
    depends on
    @bascik/bascik
    , or the root has
    bascik.config.ts
    /
    bascik.config.js
    , or there are
    .html
    files under
    src/components/
    .
  • Node.js 24.21 or newer (26, the current LTS, recommended). Node runs
    .ts
    files natively and Bascik strips browser TypeScript, so no TypeScript build step exists.
  • Standard scripts:
    npm run dev
    (
    bascik
    ),
    npm run build
    (
    bascik --build
    ),
    npm run lint
    (
    bascik --check
    ).
    dev
    is always plain
    bascik
    , also in a Cloudflare or Netlify project: never rewrite it to
    wrangler dev
    ,
    netlify dev
    , or
    bascik --target ...
    . The host's runtime gets its own script (
    dev:cloudflare
    ,
    dev:netlify
    ); see the
    bascik-config
    skill, Deploy.
  • package.json
    依赖
    @bascik/bascik
    ,或根目录包含
    bascik.config.ts
    /
    bascik.config.js
    ,或
    src/components/
    下存在
    .html
    文件。
  • Node.js 24.21 或更高版本(推荐使用 26,即当前 LTS)。Node 原生运行
    .ts
    文件,Bascik 会剥离浏览器 TypeScript,因此不需要 TypeScript 构建步骤。
  • 标准 scripts:
    npm run dev
    (
    bascik
    )、
    npm run build
    (
    bascik --build
    )、
    npm run lint
    (
    bascik --check
    )。
    dev
    始终使用普通的
    bascik
    ,在 Cloudflare 或 Netlify 项目中也是如此:绝不要改写为
    wrangler dev
    、
    netlify dev
    或
    bascik --target ...
    。host 的 runtime 应使用单独的 script(
    dev:cloudflare
    、
    dev:netlify
    );参见
    bascik-config
    skill 的 Deploy 部分。

Project layout

项目布局

text
src/
  components/            component templates; the file name is the tag name
    site-nav/
      site-nav.html      <site-nav>
      site-nav.css       optional companion stylesheet, scoped to site-nav
      site-nav.ts        optional companion script, loaded with <script src="site-nav.ts">
      links.ts           data or a helper only site-nav uses, imported with ./links.ts
  pages/                 one .html file per URL, plus public assets
    index.html           /
    about.html           /about
    blog/post.html       /blog/post
    404.html             served for unknown routes
    css/styles.css       copied to dist/css/styles.css (global, unscoped)
  lib/                   modules several components or pages share (import with @/lib/...)
  api/                   optional API route handlers in .ts (bascik --server only)
dist/                    compiled output, never edited by hand, never committed
text
src/
  components/            component templates; the file name is the tag name
    site-nav/
      site-nav.html      <site-nav>
      site-nav.css       optional companion stylesheet, scoped to site-nav
      site-nav.ts        optional companion script, loaded with <script src="site-nav.ts">
      links.ts           data or a helper only site-nav uses, imported with ./links.ts
  pages/                 one .html file per URL, plus public assets
    index.html           /
    about.html           /about
    blog/post.html       /blog/post
    404.html             served for unknown routes
    css/styles.css       copied to dist/css/styles.css (global, unscoped)
  lib/                   modules several components or pages share (import with @/lib/...)
  api/                   optional API route handlers in .ts (bascik --server only)
dist/                    compiled output, never edited by hand, never committed

Write a component

编写组件

Default pattern:
<style>
above the markup, markup in the middle, a TypeScript
<script>
below. Give every element a script must control an
id
and find it with
getElementById
, and put a
data-testid
on the root and on each control (rule 16).
html
<!-- src/components/like-button/like-button.html -->
<style>
  .like {
    padding: 0.5rem 1rem;
    border-radius: 6px;
  }
  .like.active {
    background: #d3ff8d;
  }
</style>
<button class="like" id="btn" type="button" data-testid="like-button">
  <span data-bascik-prop-label>Like</span>
</button>
<script type="text/typescript">
  const btn = document.getElementById("btn") as HTMLButtonElement;
  btn.addEventListener("click", () => btn.classList.toggle("active"));
</script>
html
<!-- src/pages/index.html -->
<like-button data-bascik-prop-label="Like this post" />
<like-button data-bascik-prop-label="Like this comment" />
Bascik strips the types, rewrites
.like
to
.bascik__like-button__like
, gives each instance its own
id
(
bascik__like-button__<instance>__btn
), rewrites the
getElementById
string and the
classList
class to match, and wraps the script in an IIFE. For a longer script, move it to
like-button.ts
beside the template and load it with
<script src="like-button.ts"></script>
. Both buttons work independently. To see the exact output, run
scripts/preview.mjs
.
New components are picked up by the running dev server automatically; no restart is needed.
默认模式是:
<style>
放在上方,markup 放在中间,TypeScript
<script>
放在下方。为每个需要由 script 控制的元素提供
id
并使用
getElementById
查找,同时在根元素和每个控件上添加
data-testid
(规则 16)。
html
<!-- src/components/like-button/like-button.html -->
<style>
  .like {
    padding: 0.5rem 1rem;
    border-radius: 6px;
  }
  .like.active {
    background: #d3ff8d;
  }
</style>
<button class="like" id="btn" type="button" data-testid="like-button">
  <span data-bascik-prop-label>Like</span>
</button>
<script type="text/typescript">
  const btn = document.getElementById("btn") as HTMLButtonElement;
  btn.addEventListener("click", () => btn.classList.toggle("active"));
</script>
html
<!-- src/pages/index.html -->
<like-button data-bascik-prop-label="Like this post" />
<like-button data-bascik-prop-label="Like this comment" />
Bascik 会剥离类型,将
.like
重写为
.bascik__like-button__like
,为每个实例生成独立的
id
(
bascik__like-button__<instance>__btn
),重写
getElementById
中的字符串和
classList
class 以保持一致,并将 script 包裹在 IIFE 中。对于更长的 script,将其移到模板旁边的
like-button.ts
,并使用
<script src="like-button.ts"></script>
加载。两个按钮都能独立工作。运行
scripts/preview.mjs
可以查看精确输出。
运行中的 dev server 会自动识别新组件,无需重启。

Rules (must follow)

规则(必须遵守)

  1. Concise, lowercase component names. Name the job, not the app:
    not-found
    , not
    shop-not-found
    ;
    navigation
    , not
    shop-nav
    . Use hyphens between words, never underscores. A non-native single word works because Bascik expands it before the browser sees it, although the CLI/editor recommend hyphens for future compatibility. Never use a native HTML, SVG, or MathML element name, including obsolete names (
    nav
    ,
    dialog
    ,
    path
    ,
    mi
    ,
    keygen
    ); use
    navigation
    ,
    site-header
    , or
    site-footer
    instead. Names must be unique across every component root. See
    /components#component-naming-and-hyphens
    .
  2. Per-instance targeting uses
    id
    .
    Use
    getElementById
    (or
    getElementsByName
    ) for elements a script controls.
    querySelector('.cls')
    returns the first instance on the page, so the other instances look broken.
  3. Order:
    <style>
    above the markup,
    <script>
    below it.
  4. Props carry text only.
    data-bascik-prop-*
    values are HTML-escaped strings. Pass markup or nested components through slots (
    data-bascik-slot
    ).
  5. Self-close components that take no slot content:
    <site-head />
    ,
    <site-footer />
    .
  6. HTML and CSS first. Reach for JavaScript only when HTML and CSS cannot do the job cleanly (
    <details>
    ,
    <dialog>
    ,
    :has()
    ,
    popover
    cover a lot).
  7. Semantic HTML. Pick the element the HTML standard defines for the job, because Bascik ships exactly the markup you write: a component's root is the semantic element itself (
    <site-nav>
    renders a
    <nav>
    , a review an
    <article>
    , a form a
    <form>
    ), not a wrapper
    <div>
    ;
    <button type="button">
    for actions and
    <a href>
    for navigation, never a clickable
    <div>
    or
    <span>
    ; one
    <main>
    and one
    <h1>
    per page with no skipped heading levels; a
    <label>
    for every control;
    alt
    on every image; native
    <details>
    ,
    <dialog>
    , and
    popover
    before ARIA or JavaScript. The checklist with spec links:
    read-doc.mjs /component-best-practices#use-htmls-elements
    .
  8. Runtime state in
    data-
    attributes.
    Toggle
    data-state="open"
    and style
    .panel[data-state="open"]
    ; anchor attribute selectors on a scoped class.
  9. Zero config. Do not create
    bascik.config.ts
    unless a requirement differs from a default, and never put
    siteUrl
    in it. See the
    bascik-config
    skill.
  10. Never write generated files into
    src/
    .
    Generated output goes to
    dist/
    (
    BASCIK_OUT_DIR
    ) from an existing
    pipeline.exec
    script.
  11. Client scripts are not bundled. Bare imports such as
    import x from 'pkg'
    fail in the browser. They work only in Node-side scripts (
    data-bascik-build
    ,
    data-bascik-server
    ). Bundle browser packages with esbuild (
    read-doc.mjs /how-to/bundling-npm-packages
    ).
  12. Pages are lists of tags. Shared
    <head>
    tags, the header, the footer, and every section with a nameable job become components (
    <site-head />
    ,
    <site-header />
    ,
    <pricing-section />
    ,
    <site-footer />
    ). Pages keep their own
    <html>
    ,
    <head>
    , and
    <body>
    . Balance it: no tiny single-use wrappers and no props passed through components that do not use them.
  13. Script directives are exclusive. One
    <script>
    tag takes at most one of
    data-bascik-build
    ,
    data-bascik-build-once
    ,
    data-bascik-routes
    ,
    data-bascik-server
    ,
    data-bascik-stream
    .
    data-bascik-build-once
    runs once per file with no page context.
  14. TypeScript by default. New browser and Node code is TypeScript (see "TypeScript by default" above). Use JavaScript only when the person asks for it.
  15. Every script and helper builds markup with
    html
    .
    Any build, build-once, routes, server or stream script, API route, and any
    .ts
    helper that returns markup (in
    src/lib/
    or beside a component) does
    import { html } from "@bascik/bascik/html"
    and returns
    html`<p>${value}</p>`
    : it escapes each value for where it lands, turns
    javascript:
    URLs into
    about:invalid
    , and throws where escaping cannot help. Use
    raw()
    only for markup the project wrote and trusts or sanitized CMS HTML,
    json()
    for JSON-LD, and
    esc()
    or
    safeUrl()
    where a string is built without
    html
    . Never write your own escaper (
    escapeHtml
    , an
    &amp;
    replace chain) and never interpolate data into a plain template string. Give a URL attribute one hole: build the URL in code first (
    const href = `${siteUrl}${path}`
    , then
    href="${href}"
    ), because
    html
    refuses two holes before the URL's first
    /
    (
    url-scheme-split
    ) so that two values cannot spell
    javascript:
    together. A browser script cannot import it: set
    textContent
    there. See the
    bascik-scripts
    skill.
  16. A
    data-testid
    on what a test needs.
    Every component root gets
    data-testid="<component-name>"
    , and every interactive part (a button, an input, select, or textarea, a
    <summary>
    , a link a script handles, an element with an ARIA widget role or
    tabindex="0"
    ) gets
    data-testid="<component-name>-<part>"
    . Forms, dialogs, and landmarks (
    <main>
    ,
    <nav>
    , the page's
    <header>
    and
    <footer>
    ) get one too. Bascik never scopes or hashes
    data-testid
    , so
    getByTestId(...)
    matches in a production build where classes and ids are hashed. Instances share their ids: a test picks one with
    .filter({ hasText })
    , or the page names one with
    data-testid
    on the usage tag, which replaces the root's. Never put request or random data in a test id, and keep existing hooks (
    data-test
    ,
    data-cy
    ,
    data-qa
    ) when migrating.
    scripts/new-component.mjs
    adds the root's, and
    scripts/verify.mjs
    lists the gaps. See
    read-doc.mjs /testing/e2e-testing
    .
  17. Colocate what one component or page uses. A module (data, helper, types) that one component uses lives in that component's folder, with its tests, and is imported with
    ./
    (
    import { faqs } from "./faqs.ts"
    ). Content lives with the component that renders it: a page that also needs it, for JSON-LD say, imports it from there (
    @/components/faq-list/faqs.ts
    ). A module only one page uses sits beside that page as a
    .ts
    file (the pages folder publishes a
    .json
    or
    .js
    file as a public asset, never a
    .ts
    one). Only modules several components or pages share, or that config, exec scripts, or API routes also import, go in
    src/lib/
    . Files in component and page folders are watched already;
    src/lib/
    needs
    pipeline.watchPaths
    .
    scripts/audit-components.mjs
    reports a
    src/lib/
    module with one user (
    single-use-module
    ).
  1. 组件名称应简洁且使用小写。 根据职责命名,而不是根据应用命名:使用
    not-found
    ,而不是
    shop-not-found
    ;使用
    navigation
    ,而不是
    shop-nav
    。单词之间使用连字符,绝不使用下划线。非原生单词可以使用,因为 Bascik 会在浏览器看到之前将其展开,不过 CLI/editor 建议使用连字符以保持未来兼容性。绝不要使用原生 HTML、SVG 或 MathML 元素名,包括过时名称(
    nav
    、
    dialog
    、
    path
    、
    mi
    、
    keygen
    );应改用
    navigation
    、
    site-header
    或
    site-footer
    。所有组件根元素的名称必须保持唯一。参见
    /components#component-naming-and-hyphens
    。
  2. 按实例定位时使用
    id
    。
    对 script 控制的元素使用
    getElementById
    (或
    getElementsByName
    )。
    querySelector('.cls')
    只返回页面上的第一个实例,因此其他实例会看起来像是失效了。
  3. 顺序:
    <style>
    放在 markup 上方,
    <script>
    放在下方。
  4. Props 只承载文本。
    data-bascik-prop-*
    的值是经过 HTML 转义的字符串。通过 slots 传递 markup 或嵌套组件(
    data-bascik-slot
    )。
  5. 不接受 slot 内容的组件应自闭合:
    <site-head />
    、
    <site-footer />
    。
  6. 优先使用 HTML 和 CSS。 只有在 HTML 和 CSS 无法干净完成任务时才使用 JavaScript(
    <details>
    、
    <dialog>
    、
    :has()
    、
    popover
    已能覆盖很多场景)。
  7. 使用语义化 HTML。 根据 HTML 标准为职责选择元素,因为 Bascik 输出的正是你编写的 markup:组件根元素本身就是语义元素(
    <site-nav>
    渲染为
    <nav>
    ,review 渲染为
    <article>
    ,form 渲染为
    <form>
    ),而不是使用
    <div>
    wrapper;操作用
    <button type="button">
    ,导航用
    <a href>
    ,绝不让
    <div>
    或
    <span>
    可点击;每页使用一个
    <main>
    和一个
    <h1>
    ,且不跳过标题级别;每个控件都有
    <label>
    ;每张图片都有
    alt
    ;优先使用原生
    <details>
    、
    <dialog>
    和
    popover
    ,然后才考虑 ARIA 或 JavaScript。检查清单及规范链接:
    read-doc.mjs /component-best-practices#use-htmls-elements
    。
  8. Runtime 状态使用
    data-
    属性。
    切换
    data-state="open"
    ,并使用
    .panel[data-state="open"]
    设置样式;在 scoped class 上使用 anchor attribute selector。
  9. 零配置。 除非需求与默认行为不同,否则不要创建
    bascik.config.ts
    ,并且绝不把
    siteUrl
    放入其中。参见
    bascik-config
    skill。
  10. 绝不把生成文件写入
    src/
    。
    生成输出应通过已有的
    pipeline.exec
    script 写入
    dist/
    (
    BASCIK_OUT_DIR
    )。
  11. 客户端 script 不会被打包。 浏览器端的裸导入(例如
    import x from 'pkg'
    )会失败。它们只适用于 Node 端 script(
    data-bascik-build
    、
    data-bascik-server
    )。使用 esbuild 打包浏览器依赖(
    read-doc.mjs /how-to/bundling-npm-packages
    )。
  12. 页面是标签列表。 共享的
    <head>
    标签、header、footer,以及每个具有明确职责的区块都应成为组件(
    <site-head />
    、
    <site-header />
    、
    <pricing-section />
    、
    <site-footer />
    )。页面保留自己的
    <html>
    、
    <head>
    和
    <body>
    。保持平衡:不要创建微型单用途 wrapper,也不要把 props 传递给根本不会使用它们的组件。
  13. Script 指令互斥。 一个
    <script>
    标签最多只能使用
    data-bascik-build
    、
    data-bascik-build-once
    、
    data-bascik-routes
    、
    data-bascik-server
    、
    data-bascik-stream
    中的一个。
    data-bascik-build-once
    每个文件只运行一次,且没有页面上下文。
  14. 默认使用 TypeScript。 所有新的浏览器端和 Node 端代码都使用 TypeScript(参见上文“默认使用 TypeScript”)。只有在用户要求时才使用 JavaScript。
  15. 每个 script 和 helper 都使用
    html
    构造 markup。
    任何 build、build-once、routes、server 或 stream script、API route,以及任何返回 markup 的
    .ts
    helper(位于
    src/lib/
    或组件旁)都要
    import { html } from "@bascik/bascik/html"
    ,并返回
    html`<p>${value}</p>`
    :它会针对每个值最终落入的位置进行转义,将
    javascript:
    URL 转换为
    about:invalid
    ,并在无法通过转义解决问题时抛出异常。仅对项目自行编写且可信的 markup 或经过清理的 CMS HTML 使用
    raw()
    ,对 JSON-LD 使用
    json()
    ,对未通过
    html
    构建的字符串使用
    esc()
    或
    safeUrl()
    。绝不要自行编写 escaper(
    escapeHtml
    、替换
    &amp;
    的链式处理),也绝不要把数据插入普通模板字符串。URL 属性只留一个 hole:先在代码中构建 URL(
    const href = `${siteUrl}${path}`
    ),然后使用
    href="${href}"
    ,因为
    html
    会拒绝在 URL 第一个
    /
    之前出现两个 hole,从而避免两个值拼成
    javascript:
    。浏览器 script 无法导入它:请在那里设置
    textContent
    。参见
    bascik-scripts
    skill。
  16. 测试所需元素都要有
    data-testid
    。
    每个组件根元素都要有
    data-testid="<component-name>"
    ,每个交互部分(button、input、select、textarea、
    <summary>
    、由 script 处理的链接、带 ARIA widget role 或
    tabindex="0"
    的元素)都要有
    data-testid="<component-name>-<part>"
    。表单、dialog 和 landmarks(
    <main>
    、
    <nav>
    、页面的
    <header>
    和
    <footer>
    )也要添加。Bascik 不会对
    data-testid
    进行作用域处理或哈希,因此即使 class 和 id 在生产构建中经过哈希,
    getByTestId(...)
    仍然可以匹配。多个实例会共享 id:测试可以通过
    .filter({ hasText })
    选择其中一个,或者在页面的使用标签上设置
    data-testid
    来命名某个实例,该值会替换根元素上的值。测试 id 中绝不要包含请求数据或随机数据,迁移时还应保留已有测试钩子(
    data-test
    、
    data-cy
    、
    data-qa
    )。
    scripts/new-component.mjs
    会为根元素添加测试 id,
    scripts/verify.mjs
    会列出缺失项。参见
    read-doc.mjs /testing/e2e-testing
    。
  17. 将仅由一个组件或页面使用的内容放在一起。 仅由一个组件使用的模块(数据、helper、类型)应放在该组件的文件夹中,与测试放在一起,并通过
    ./
    导入(
    import { faqs } from "./faqs.ts"
    )。内容应与渲染它的组件放在一起:如果页面也需要该内容,例如用于 JSON-LD,就从组件目录导入(
    @/components/faq-list/faqs.ts
    )。仅由一个页面使用的模块应作为
    .ts
    文件放在页面旁(pages 文件夹会将
    .json
    或
    .js
    文件作为公共资产发布,但绝不会发布
    .ts
    文件)。只有多个组件或页面共享的模块,或者 config、exec script 和 API route 也会导入的模块,才应放入
    src/lib/
    。组件和页面文件夹中的文件已经被监听;
    src/lib/
    需要配置
    pipeline.watchPaths
    。
    scripts/audit-components.mjs
    会报告只有一个使用者的
    src/lib/
    模块(
    single-use-module
    )。

Slots, props, and attributes

Slots、Props 和属性

NeedComponent templateUsage site
Default slot (rich HTML)
<div data-bascik-slot>fallback</div>
<my-card><h2>Title</h2></my-card>
Named slot
<div data-bascik-slot="header"></div>
<div data-bascik-slot="header"><h1>Hi</h1></div>
inside the tag
Text prop
<p data-bascik-prop-title></p>
<my-card data-bascik-prop-title="Hello" />
Prop into an attribute
<img data-bascik-attr-src="image" data-bascik-attr-alt="alt">
<my-card data-bascik-prop-image="/a.png" data-bascik-prop-alt="A" />
Forward attributes(nothing)
<my-card class="wide" aria-label="Promo" />
lands on the first root element
Pass props as
data-bascik-prop-<name>="..."
on the usage tag. A plain attribute such as
label="..."
is not a prop: it is forwarded to the root element and the prop keeps its default. A forwarded attribute the root already has keeps the root's value, except
data-testid
: the usage tag's replaces it, so a page can name one instance. The slot element itself is replaced by the content, so named-slot wrappers disappear: wrap headings and links in a
<div data-bascik-slot="x">
rather than putting the attribute on the
<h1>
or
<a>
. Details:
read-doc.mjs /slots
,
/props
,
/attribute-inheritance
.
需求组件模板使用位置
默认 slot(丰富 HTML)
<div data-bascik-slot>fallback</div>
<my-card><h2>Title</h2></my-card>
命名 slot
<div data-bascik-slot="header"></div>
在标签内部使用
<div data-bascik-slot="header"><h1>Hi</h1></div>
文本 prop
<p data-bascik-prop-title></p>
<my-card data-bascik-prop-title="Hello" />
将 prop 放入 attribute
<img data-bascik-attr-src="image" data-bascik-attr-alt="alt">
<my-card data-bascik-prop-image="/a.png" data-bascik-prop-alt="A" />
转发属性(无)
<my-card class="wide" aria-label="Promo" />
会落在第一个根元素上
在使用标签上通过
data-bascik-prop-<name>="..."
传递 props。普通 attribute(例如
label="..."
)不是 prop:它会被转发到根元素,而 prop 会保留默认值。根元素已经存在的转发属性会保留根元素的值,但
data-testid
除外:使用标签上的值会替换它,因此页面可以命名某个实例。slot 元素本身会被内容替换,因此命名 slot 的 wrapper 会消失:请将 heading 和 link 包装在
<div data-bascik-slot="x">
中,而不是直接在
<h1>
或
<a>
上设置该 attribute。详情参见
read-doc.mjs /slots
、
/props
、
/attribute-inheritance
。

Script tags at a glance

Script 标签一览

TagRunsUse for
<script>
BrowserComponent and page behavior (scoped, IIFE-wrapped)
<script data-bascik-build>
Node, at build timeMarkup from files, Markdown, APIs; output replaces the tag
<script data-bascik-build-once>
Node, once per fileMarkup shared by every use of a component or every route of a template
<script data-bascik-routes>
Node, at build timeExpanding
[slug].html
templates into many pages
<script data-bascik-server>
Node, per requestPersonalized markup (needs
bascik --server
or a serverless target)
<script data-bascik-stream>
Node, per request, streamedSlow per-request blocks that should not delay the page shell
Node-side scripts export a default function that returns an HTML string. Component tags in that HTML render, at build time and, from server and stream scripts, per request. Details are in the
bascik-scripts
skill.
标签运行方式用途
<script>
浏览器组件和页面行为(作用域隔离、IIFE 包裹)
<script data-bascik-build>
Node,在构建时来自文件、Markdown、API 的 markup;输出会替换该标签
<script data-bascik-build-once>
Node,每个文件运行一次组件的每次使用或模板的每个路由共用的 markup
<script data-bascik-routes>
Node,在构建时将
[slug].html
模板扩展为多个页面
<script data-bascik-server>
Node,每次请求个性化 markup(需要
bascik --server
或 serverless target)
<script data-bascik-stream>
Node,每次请求,流式传输不应延迟页面外壳的慢速单请求代码块
Node 端 script 导出一个返回 HTML 字符串的默认函数。其 HTML 中的组件标签会在构建时渲染;从 server 和 stream script 渲染时,则在每次请求中渲染。详情参见
bascik-scripts
skill。

CLI

CLI

sh
bascik                      # dev server at http://localhost:8080 with live reload and watch
bascik --build              # production build to dist/ (HTML, CSS, JS, and identifier minification on)
bascik --build --only "blog/**"   # rebuild matching pages without cleaning dist/
bascik --server             # serve a built dist/ (needed for server/stream scripts and API routes)
bascik --check              # project checks, plus the per-file checks when @bascik/language-server is installed
bascik --check --fix        # also rewrite fixable findings (needs @bascik/language-server)
bascik add <package>        # copy components from an installed npm package into src/components/
bascik init                 # add Bascik to an existing project
npx @bascik/language-server --check   # the per-file checks on their own (bascik --check already runs them)
bascik --check
and the linter are not two overlapping tools.
bascik --check
answers project questions (duplicate routes, unused components, config), and the linter answers per-file questions (attribute typos, CSS that cannot be scoped).
bascik --check
runs the linter too, so use it, and reach for the linter alone only on a folder outside the project.
New project:
npm create bascik@rc my-site -y
. It and
bascik init
both write
.vscode/settings.json
with
"html.validate.scripts": false
and recommend the Bascik extension in
.vscode/extensions.json
, merging into files the folder already has. Third-party linters such as Webhint are opt-in: neither command installs them or creates or modifies their configs. A project set up any other way (a migration, or files copied in with
add-example.mjs
) needs that VS Code setting added by hand, or VS Code joins a page's
<script>
blocks into one file and reports false errors such as "A module cannot have multiple default exports". The setting turns off only VS Code's own check of inline scripts, not Bascik's checks. Production builds need a site URL for
sitemap.xml
and
robots.txt
:
BASCIK_SITE_URL=https://example.com bascik --build
(or
--site-url
, or
.env
). Full CLI and error output:
read-doc.mjs /cli
.
sh
bascik                      # dev server at http://localhost:8080 with live reload and watch
bascik --build              # production build to dist/ (HTML, CSS, JS, and identifier minification on)
bascik --build --only "blog/**"   # rebuild matching pages without cleaning dist/
bascik --server             # serve a built dist/ (needed for server/stream scripts and API routes)
bascik --check              # project checks, plus the per-file checks when @bascik/language-server is installed
bascik --check --fix        # also rewrite fixable findings (needs @bascik/language-server)
bascik add <package>        # copy components from an installed npm package into src/components/
bascik init                 # add Bascik to an existing project
npx @bascik/language-server --check   # the per-file checks on their own (bascik --check already runs them)
bascik --check
与 linter 不是两个功能重叠的工具。
bascik --check
回答项目层面的问题(重复路由、未使用组件、配置),而 linter 回答单文件层面的问题(属性拼写错误、无法作用域化的 CSS)。
bascik --check
也会运行 linter,因此应使用它;只有在项目外的文件夹上才单独使用 linter。
新项目:运行
npm create bascik@rc my-site -y
。它和
bascik init
都会写入
.vscode/settings.json
,其中包含
"html.validate.scripts": false
,并在
.vscode/extensions.json
中推荐 Bascik extension,同时与文件中已有的设置合并。Webhint 等第三方 linter 属于可选项:这两个命令既不会安装它们,也不会创建或修改其配置。以其他方式设置的项目(例如迁移项目,或通过
add-example.mjs
复制文件的项目)需要手动添加该 VS Code setting,否则 VS Code 会将一个页面的多个
<script>
代码块合并为一个文件,并报告类似“A module cannot have multiple default exports”的虚假错误。该 setting 只会关闭 VS Code 自己对内联 script 的检查,不会关闭 Bascik 的检查。生产构建需要 site URL 来生成
sitemap.xml
和
robots.txt
:使用
BASCIK_SITE_URL=https://example.com bascik --build
(或
--site-url
、
.env
)。完整 CLI 和错误输出参见
read-doc.mjs /cli
。

Verify every change

验证每次修改

Do not stop at "the file looks right". Run these from the project root:
  1. node <skill-dir>/scripts/verify.mjs
    runs
    bascik --check
    (project and per-file checks) and, for component folders outside the project, the linter, then summarizes each step. Add
    --build
    to also run a production build.
  2. node <skill-dir>/scripts/audit-components.mjs
    reports oversized pages and components, inline sections that should be components, repeated blocks and heading groups, the same fact typed into several files, props every page passes with the same value, tiny single-use components, props passed through layers, JavaScript where TypeScript is expected, a hand-written HTML escaper or code that strips
    <
    ,
    >
    and
    &
    from a value (build the markup with
    html
    instead), a
    src/lib/
    module that only one component or page uses (move it beside that user), and a component build script that should be
    data-bascik-build-once
    (or a build-once script that reads the page). It also warns about semantic HTML: a clickable
    <div>
    or
    <span>
    , a component root that is a
    <div>
    where
    <nav>
    ,
    <header>
    ,
    <footer>
    ,
    <form>
    ,
    <article>
    , or
    <section>
    fits, a skipped heading level in a page's output, an
    <img>
    with no
    alt
    , a form control with no label, and a page with no
    <main>
    . Each finding has
    file:line
    and what to do. Pass
    --allow-js
    only when the person asked for JavaScript.
  3. node <skill-dir>/scripts/preview.mjs '<my-card data-bascik-prop-title="Hi" />' --count 2
    compiles a snippet with the project's real components and config and prints the unminified output, so you can confirm scoped names, slot placement, and script rewrites.
  4. Read the relevant file in
    dist/
    after a build. A hyphenated tag still present in
    dist/
    means no component matched it.
A page is not done until
verify.mjs
passes and
audit-components.mjs
exits 0. Fix each warning, or tell the person which one you kept and why (for example, a long legal page that is all prose). "The content differs" is not a reason to keep a repeated block: different text is what props and slots are for. Neither is "the fields belong to their form" when two forms repeat them.
<skill-dir>
is the directory containing this file, for example
.agents/skills/bascik
.
不要停在“文件看起来没问题”这一步。从项目根目录运行以下命令:
  1. node <skill-dir>/scripts/verify.mjs
    会运行
    bascik --check
    (项目检查和单文件检查),并对项目外的组件文件夹运行 linter,然后汇总每个步骤。添加
    --build
    还会运行生产构建。
  2. node <skill-dir>/scripts/audit-components.mjs
    会报告过大的页面和组件、应提取为组件的内联区块、重复区块和标题组、多个文件中重复输入的同一事实、每个页面都传入相同值的 props、微型单用途组件、跨层传递的 props、预期使用 TypeScript 却使用 JavaScript 的情况、手写 HTML escaper 或会从值中移除
    <
    、
    >
    和
    &
    的代码(应使用
    html
    构造 markup)、仅由一个组件或页面使用的
    src/lib/
    模块(应移到该使用者旁),以及应使用
    data-bascik-build-once
    的组件构建 script(或读取页面的 build-once script)。它还会警告语义化 HTML 问题:可点击的
    <div>
    或
    <span>
    、本应使用
    <nav>
    、
    <header>
    、
    <footer>
    、
    <form>
    、
    <article>
    或
    <section>
    却使用
    <div>
    的组件根元素、页面输出中跳过的标题级别、缺少
    alt
    的
    <img>
    、没有 label 的表单控件,以及缺少
    <main>
    的页面。每项发现都会提供
    file:line
    和处理建议。只有在用户要求 JavaScript 时才传入
    --allow-js
    。
  3. node <skill-dir>/scripts/preview.mjs '<my-card data-bascik-prop-title="Hi" />' --count 2
    会使用项目真实的组件和配置编译一个代码片段,并打印未压缩输出,从而确认 scoped 名称、slot 位置和 script 重写结果。
  4. 构建后读取
    dist/
    中的相关文件。如果带连字符的标签仍然出现在
    dist/
    中,说明没有组件与它匹配。
只有当
verify.mjs
通过且
audit-components.mjs
以状态码 0 退出时,页面才算完成。修复每项警告,或者告诉用户你保留了哪项警告及其原因(例如全部为正文的长法律页面)。“内容不同”不能成为保留重复区块的理由:不同文本正是 props 和 slots 的用途。如果两个重复表单包含相同字段,“这些字段属于它们的表单”也不能成为保留重复区块的理由。
<skill-dir>
是包含该文件的目录,例如
.agents/skills/bascik
。

Editor linters read source; Bascik writes
dist/

Editor linter 读取源码;Bascik 写入
dist/

webhint, axe, html-validate, HTMLHint, and VS Code's HTML checker read
src/
. Bascik overrules some of what they report, because the answer is only in the built page:
  • Composed at build time: a missing charset or viewport
    <meta>
    (a head component adds them), an
    apple-touch-icon
    link "should be specified in the
    <head>
    " (a head component is a fragment with no
    <head>
    ), a missing
    <title>
    (a build script writes it), an
    <img>
    with no
    alt
    or
    src
    and an empty heading whose value comes from a
    data-bascik-attr-*
    or
    data-bascik-prop-*
    prop.
  • Bascik syntax: self-closed component tags, component files with no doctype, and several
    <script>
    blocks in one file (
    "html.validate.scripts": false
    ).
Rules:
  • Never "fix" one of these by adding the tag to a source page. Open the built page in
    dist/
    first. If the tag is there, the warning is false and a second copy would ship twice; if it is missing there too, fix the head component or build script.
  • Everything else is real: a
    <button>
    with no
    type
    ,
    <html>
    with no
    lang
    , a literal
    <img>
    with no
    alt
    . The optional documented
    .hintrc
    turns
    image-alt
    off in the editor, so a literal missing
    alt
    shows only in
    audit-components.mjs
    and the built-site check: run them. The documented
    src/.htmlvalidate.json
    makes
    <img>
    src
    optional, so a literal
    <img>
    with no
    src
    shows only when html-validate checks
    dist/
    ; never turn
    element-required-attributes
    off for all of
    src/
    , which also hides a missing
    lang
    .
  • Do not add third-party linter configs or dependencies by default. If the person uses Webhint or another linter, read
    /testing/linting#editor-linters-read-source
    and adapt its copyable configs to the project's folders. Preserve existing settings. Keep Webhint configs as plain JSON, because its extension's quick fixes use
    JSON.parse
    . Keep the built-site config independent of the source config so source exceptions do not leak into
    dist/
    . Other linters get a
    src/
    config; HTMLHint's must list its default rules too, or unnamed checks are disabled.
  • The checks that decide run on built output:
    bascik --check
    for Bascik syntax and WHATWG parsing, then
    bascik --build
    . If the person opts into Webhint, copy the documented
    .hintrc.dist.json
    first, then run
    npx hint -c .hintrc.dist.json $(find dist -name '*.html')
    (files, not the folder; use
    directory.out
    if it is not
    dist
    ). Lighthouse on
    bascik --server
    is another built-site audit. Webhint does not check local links: run
    check-links.mjs
    .
  • cSpell
    Unknown word
    on a brand or product name is real:
    add the name to the project's
    cspell.json
    "words"
    , not to a user setting.
Every warning, its setting, and the config files:
read-doc.mjs /testing/linting#editor-linters-read-source
.
webhint、axe、html-validate、HTMLHint 和 VS Code HTML checker 读取
src/
。Bascik 会推翻其中一部分报告,因为答案只存在于构建后的页面中:
  • 在构建时组合: 缺少 charset 或 viewport
    <meta>
    (head component 会添加它们)、apple-touch-icon link 被提示“should be specified in the
    <head>
    ”(head component 是没有
    <head>
    的 fragment)、缺少
    <title>
    (由 build script 写入)、
    <img>
    缺少
    alt
    或
    src
    ,以及空 heading 的值来自
    data-bascik-attr-*
    或
    data-bascik-prop-*
    prop。
  • Bascik 语法: 自闭合组件标签、没有 doctype 的组件文件,以及一个文件中的多个
    <script>
    代码块(
    "html.validate.scripts": false
    )。
规则:
  • 绝不要通过向源页面添加标签来“修复”这些问题。 应先打开
    dist/
    中的构建页面。如果标签在那里,则该警告是误报,再添加一份会导致线上页面出现重复内容;如果构建页面中也缺少它,则应修复 head component 或 build script。
  • 其他问题都是真实问题: 没有
    type
    的
    <button>
    、没有
    lang
    的
    <html>
    、缺少
    alt
    的字面量
    <img>
    。可选的文档化
    .hintrc
    会关闭编辑器的
    image-alt
    规则,因此字面量中缺少
    alt
    只会出现在
    audit-components.mjs
    和构建站点检查中:应运行这些检查。文档化的
    src/.htmlvalidate.json
    会让
    <img>
    的
    src
    变成可选,因此字面量中缺少
    src
    只会出现在针对
    dist/
    的 html-validate 检查中;绝不要为所有
    src/
    关闭
    element-required-attributes
    ,否则也会隐藏缺少
    lang
    的问题。
  • 默认不要添加第三方 linter 配置或依赖。 如果用户使用 Webhint 或其他 linter,请阅读
    /testing/linting#editor-linters-read-source
    并调整其中可复制的配置以适配项目文件夹。保留已有设置。Webhint 配置应保持为普通 JSON,因为其 extension 的快速修复使用
    JSON.parse
    。构建站点配置应独立于源码配置,避免源码例外泄漏到
    dist/
    。其他 linter 使用
    src/
    配置;HTMLLint 的配置还必须列出其默认规则,否则未命名的检查会被禁用。
  • 决定性检查应针对构建输出运行: 先运行
    bascik --check
    检查 Bascik 语法和 WHATWG 解析,再运行
    bascik --build
    。如果用户选择使用 Webhint,请先复制文档化的
    .hintrc.dist.json
    ,然后运行
    npx hint -c .hintrc.dist.json $(find dist -name '*.html')
    (使用文件而不是文件夹;如果不是
    dist
    ,则使用
    directory.out
    )。在
    bascik --server
    上运行 Lighthouse 是另一种构建站点审计方式。Webhint 不会检查本地链接:请运行
    check-links.mjs
    。
  • cSpell 对品牌或产品名称报告
    Unknown word
    是真实问题:
    将该名称添加到项目的
    cspell.json
    "words"
    中,而不是添加到用户设置中。
每项警告、对应设置和配置文件参见
read-doc.mjs /testing/linting#editor-linters-read-source
。

Bundled scripts

随附 scripts

All scripts are dependency-free Node.js (24.21+) and run from the project root. Each prints
--help
.
ScriptPurpose
scripts/verify.mjs
bascik --check
, plus the linter on component folders outside the project, with an optional
--build
scripts/audit-components.mjs
Page and component structure (too big, inline sections, repeats, tiny wrappers, prop drilling), semantic HTML (clickable divs, wrapper roots, heading order, alt, labels,
<main>
), JS where TS is expected, hand-written escapers, single-use
src/lib/
modules, and build vs build-once
scripts/preview.mjs
Compile a snippet, file, or existing page in a temporary build and print the result
scripts/list-components.mjs
Inventory components with their props, slots, companion files, and
@bascik
descriptions
scripts/new-component.mjs
Create a component folder that follows the naming and ordering rules
scripts/search-docs.mjs
Search the live docs at bascik.dev for a topic
scripts/read-doc.mjs
Print the current text of a docs page, such as
/scoped-javascript
scripts/add-example.mjs
Copy a tested example component or part of the starter site from bascik.dev into the project (
--list
shows them)
scripts/check-links.mjs
Find broken internal links, missing assets, and dead
#fragments
in the built
dist/
, honoring
base
Run
list-components.mjs
before creating a component so you reuse what exists instead of duplicating it.
所有 script 都是无依赖的 Node.js(24.21+),并从项目根目录运行。每个 script 都支持
--help
。
Script用途
scripts/verify.mjs
运行
bascik --check
,并对项目外的组件文件夹运行 linter,可选
--build
scripts/audit-components.mjs
检查页面和组件结构(过大、内联区块、重复、微型 wrapper、prop drilling)、语义化 HTML(可点击 div、wrapper 根元素、标题顺序、alt、label、
<main>
)、预期使用 TS 却使用 JS、手写 escaper、单用途
src/lib/
模块,以及 build 与 build-once 的使用方式
scripts/preview.mjs
在临时构建中编译代码片段、文件或现有页面,并打印结果
scripts/list-components.mjs
盘点组件及其 props、slots、配套文件和
@bascik
描述
scripts/new-component.mjs
创建遵循命名和排序规则的组件文件夹
scripts/search-docs.mjs
在 bascik.dev 的在线文档中搜索主题
scripts/read-doc.mjs
打印当前文档页面的文本,例如
/scoped-javascript
scripts/add-example.mjs
将 bascik.dev 中经过测试的示例组件或 starter site 的部分内容复制到项目中(
--list
可列出所有内容)
scripts/check-links.mjs
在构建后的
dist/
中查找损坏的内部链接、缺失的资源和失效的
#fragment
,并遵循
base
创建组件前先运行
list-components.mjs
,以复用已有组件而不是创建重复组件。

Look it up in the docs

在文档中查找信息

The docs at https://bascik.dev are the reference; this skill only holds the rules. Read the page for any task below before writing code, instead of guessing:
sh
node <skill-dir>/scripts/read-doc.mjs /scripts/dynamic-routes
node <skill-dir>/scripts/search-docs.mjs "dynamic routes trailing slash"
How do I...Read
Write components, slots, props, attributes
/components
,
/slots
,
/props
,
/attribute-inheritance
Pick semantic elements, landmarks, headings, labels
/component-best-practices#use-htmls-elements
Pass shared
<head>
tags
/how-to/templating
Use a library (petite-vue, Alpine.js, HTMX, Tailwind)
/libraries
Use a third-party web component
/how-to/third-party-web-components
Share components between projects
/how-to/sharing-components
,
/how-to/publishing-components
Find answers to edge cases, fix a known problem
/faq
,
/testing/troubleshooting
Use the CLI and the dev server
/cli
,
/development-server
The full page index is at https://bascik.dev/llms.txt.
https://bascik.dev 上的文档是权威参考;本 skill 只保存规则。完成以下任务时,应在编写代码前阅读对应页面,而不要猜测:
sh
node <skill-dir>/scripts/read-doc.mjs /scripts/dynamic-routes
node <skill-dir>/scripts/search-docs.mjs "dynamic routes trailing slash"
如何完成……阅读
编写组件、slots、props、attributes
/components
、
/slots
、
/props
、
/attribute-inheritance
选择语义化元素、landmarks、headings、labels
/component-best-practices#use-htmls-elements
传递共享的
<head>
标签
/how-to/templating
使用库(petite-vue、Alpine.js、HTMX、Tailwind)
/libraries
使用第三方 web component
/how-to/third-party-web-components
在项目之间共享组件
/how-to/sharing-components
、
/how-to/publishing-components
查找边缘情况的答案、修复已知问题
/faq
、
/testing/troubleshooting
使用 CLI 和 dev server
/cli
、
/development-server
完整页面索引位于 https://bascik.dev/llms.txt。

Start from an example

从示例开始

https://bascik.dev/example-components has tested components (tabs, dialog, popover menu, copy button, filter list, contact form, theme toggle).
add-example.mjs
also offers
starter-site
, a complete site (layout components, SEO helper, Markdown blog, feed,
llms.txt
, contact page) whose README is at https://github.com/bascikdev/bascik/tree/main/examples/starter-site. Copy instead of writing from scratch, then adapt:
sh
node <skill-dir>/scripts/add-example.mjs --list
node <skill-dir>/scripts/add-example.mjs tab-group
node <skill-dir>/scripts/add-example.mjs starter-site/src/lib/seo.ts
The script downloads from bascik.dev, maps files to the project's configured folders, also copies example components a file uses, and writes nothing if any file would overwrite a different one. It prints
bascik.config.ts
and
package.json
instead of copying them.
https://bascik.dev/example-components 提供了经过测试的组件(tabs、dialog、popover menu、copy button、filter list、contact form、theme toggle)。
add-example.mjs
还提供
starter-site
,这是一个完整站点(布局组件、SEO helper、Markdown blog、feed、
llms.txt
、contact page),其 README 位于 https://github.com/bascikdev/bascik/tree/main/examples/starter-site。请复制而不是从零编写,然后进行适配:
sh
node <skill-dir>/scripts/add-example.mjs --list
node <skill-dir>/scripts/add-example.mjs tab-group
node <skill-dir>/scripts/add-example.mjs starter-site/src/lib/seo.ts
该 script 会从 bascik.dev 下载文件,将它们映射到项目配置的文件夹,还会复制文件所使用的示例组件;如果任何文件会覆盖内容不同的已有文件,则不会写入任何内容。它会打印
bascik.config.ts
和
package.json
,而不是复制它们。

Build a new site

构建新站点

  1. npm create bascik@rc my-site -y
    , or read the existing project first with
    list-components.mjs
    . Add
    BASCIK_SITE_URL=https://example.com
    to
    .env
    .
  2. Plan the pages and URLs (one purpose and one
    <h1>
    each), then the components each page is made of: the shared layout (head, header, footer), one component per section with a job, and one per repeated block. Plan repeated content (a collection in
    content/
    ) and anything that needs a server (API routes, per-request markup). Ask when the plan is unclear.
  3. Copy the foundation:
    add-example.mjs starter-site
    , or only the parts you need. Then replace the example name, navigation (
    src/components/site-header/links.ts
    ), colors (tokens in
    src/pages/css/styles.css
    ), and every page's
    seo({ title, description })
    .
  4. Add components (
    new-component.mjs --script inline
    or
    --script ts
    ), then pages as lists of their tags, and interactive examples. Load the companion skill for each area.
  5. Before launch:
    npx bascik --build
    , then
    verify.mjs
    ,
    audit-components.mjs
    ,
    check-links.mjs
    , the
    bascik-seo
    audit, and the
    bascik-components
    token check. The starter site README's "Build and Check It" section has the manual checklist.
  1. 运行
    npm create bascik@rc my-site -y
    ,或者先阅读现有项目并运行
    list-components.mjs
    。将
    BASCIK_SITE_URL=https://example.com
    添加到
    .env
    。
  2. 规划页面和 URL(每个页面只有一个用途和一个
    <h1>
    ),然后规划组成每个页面的组件:共享布局(head、header、footer)、每个区块对应一个具有明确职责的组件,以及每个重复区块对应一个组件。规划重复内容(
    content/
    中的集合)和所有需要 server 的内容(API routes、单请求 markup)。如果计划不明确,应向用户询问。
  3. 复制基础结构:
    add-example.mjs starter-site
    ,或只复制所需部分。然后替换示例名称、导航(
    src/components/site-header/links.ts
    )、颜色(
    src/pages/css/styles.css
    中的 tokens),以及每个页面的
    seo({ title, description })
    。
  4. 添加组件(
    new-component.mjs --script inline
    或
    --script ts
    ),然后将页面编写为标签列表,并添加交互示例。为每个领域加载配套 skill。
  5. 启动前:运行
    npx bascik --build
    ,然后运行
    verify.mjs
    、
    audit-components.mjs
    、
    check-links.mjs
    、
    bascik-seo
    审计和
    bascik-components
    token 检查。starter site README 中的“Build and Check It”部分列出了手动检查清单。

Companion skills

配套 Skills

SkillLoad it when
bascik-components
Deciding what becomes a component, writing component CSS or TypeScript, interactivity, theming, scoped names
bascik-images
Responsive WebP, native srcset and sizes, stable gallery geometry, and correct selected variants before first paint
bascik-compatibility
Deciding whether an HTML, CSS, or JavaScript feature works in browsers and inside a component
bascik-scripts
Using
data-bascik-build
,
-routes
,
-server
,
-stream
, API routes, Markdown, or a blog
bascik-seo
Titles, descriptions, canonical URLs, social cards, JSON-LD, sitemap,
llms.txt
bascik-config
Creating or editing
bascik.config.ts
, monorepos, deploying, or running
bascik --server
bascik-testing
Writing tests, debugging, validating output, or tuning Lighthouse scores
bascik-migrate
Moving a site from React, Next.js, Vue, Svelte, Astro, Eleventy, Hugo, or WordPress
bascik-refactor
Opt-in: having coding agents refactor a live site to production parity and 100 in all five Lighthouse categories
New projects do not include
bascik-refactor
. Install it with
npx skills add bascikdev/bascik --skill bascik-refactor
.
If the companions or this skill's
scripts/
folder are missing, the skill was installed as a single file. Reinstall the full set:
sh
npx skills add bascikdev/bascik
Skill何时加载
bascik-components
决定哪些内容应成为组件、编写组件 CSS 或 TypeScript、交互、主题、scoped 名称
bascik-images
响应式 WebP、原生 srcset 和 sizes、稳定的图库几何结构,以及首次绘制前选择正确变体
bascik-compatibility
判断 HTML、CSS 或 JavaScript 功能是否可以在浏览器和组件内部正常工作
bascik-scripts
使用
data-bascik-build
、
-routes
、
-server
、
-stream
、API routes、Markdown 或 blog
bascik-seo
Titles、descriptions、canonical URLs、social cards、JSON-LD、sitemap、
llms.txt
bascik-config
创建或编辑
bascik.config.ts
、monorepo、部署,或运行
bascik --server
bascik-testing
编写测试、调试、验证输出,或调整 Lighthouse 分数
bascik-migrate
从 React、Next.js、Vue、Svelte、Astro、Eleventy、Hugo 或 WordPress 迁移站点
bascik-refactor
可选:让 coding agent 将线上站点重构到功能与生产环境一致,并使五项 Lighthouse 指标全部达到 100 分
新项目不包含
bascik-refactor
。使用
npx skills add bascikdev/bascik --skill bascik-refactor
安装。
如果缺少配套 skills 或本 skill 的
scripts/
文件夹,说明该 skill 是以单个文件形式安装的。请重新安装完整集合:
sh
npx skills add bascikdev/bascik

or, without npx skills:

or, without npx skills:

mkdir -p .agents/skills && curl -fsSL https://bascik.dev/assets/skills.tar.gz | tar -xz -C .agents/skills
undefined
mkdir -p .agents/skills && curl -fsSL https://bascik.dev/assets/skills.tar.gz | tar -xz -C .agents/skills
undefined