TSSWF Readme

<- Back to home

TSSWF

Tom's Stupid-Simple Web Framework - A minimal static site generator with component support, CSS scoping, and conditional rendering.

Project Structure

src/
├── skeleton.html        # Base template for all pages
├── *.css                # Global stylesheets (copied to dist/)
├── *.js                 # Global scripts (copied to dist/)
├── pages/
│   └── *.html           # Page content files
├── components/
│   └── button/          # Component directory (name = folder name)
│       ├── button.html  # Component template (required)
│       ├── button.css   # Component styles (optional, auto-scoped)
│       └── button.js    # Component script (optional, inlined)
└── static/
    └── *                # Static assets (images, fonts, etc.)

dist/                    # Build output
├── *.html
├── *.css
├── *.js
└── static/

Usage

python build.py [project_path]

project_path is the root directory containing an src/ folder. Optional (default = current directory)

Pages

Pages live in src/pages/ and are injected into skeleton.html. Each page can define metadata using directives at the top of the file:

#PAGE_TITLE:About Us
#PAGE_HEAD_TITLE:About

<p>This is the about page content.</p>
Directive Description
#PAGE_TITLE: Page title (available as #PAGE_TITLE in skeleton)
#PAGE_HEAD_TITLE: Browser tab title (falls back to #PAGE_TITLE if omitted)

Skeleton

The skeleton (src/skeleton.html) is the base template. Use these placeholders:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=yes", target-densitydpi=device-dpi />

    <title>#PAGE_HEAD_TITLE | My Website</title>

    <link rel="stylesheet" href="styles.css?v=#CSS_HASH">
  </head>
  <body data-page="readme">
    <h1>#PAGE_TITLE</h1>
    <main>
      #PAGE_CONTENT
    </main>
  </body>
</html>
Placeholder Replaced With
#PAGE_HEAD_TITLE Value from page directive
#PAGE_TITLE Value from page directive
#PAGE_CONTENT The page's HTML content
#PAGE_ID Filename without extension (e.g., about for about.html)
#CSS_HASH Random cache-busting hash

Components

Components are reusable HTML fragments. Create a folder in src/components/ with matching HTML file:

src/components/card/
├── card.html    # Required
├── card.css     # Optional
└── card.js      # Optional

Using Components

<!-- Self-closing (no children) -->
<c-card title="Hello" />

<!-- With children -->
<c-card title="Hello">
    <p>Card content goes here.</p>
</c-card>

Defining Components

card.html:

#INPUT title
#INPUT subtitle

<div class="card">
    <h2>{title}</h2>
    <h3>{subtitle}</h3>
    <div class="card-body">
        {children}
    </div>
</div>

Component CSS

Component CSS is automatically scoped. Class selectors get a unique suffix to prevent collisions:

card.css:

.card { border: 1px solid #ccc; }
.card-body { padding: 1rem; }

Becomes something like:

.card_84721 { border: 1px solid #ccc; }
.card-body_84721 { padding: 1rem; }

The HTML class references are updated to match.

Component JS

If a component has a .js file, it's inlined as a <script> tag after the component HTML.

Conditionals

Use @IF / @ELSE blocks for conditional rendering. These work in both pages and the skeleton.

@IF(#PAGE_ID == "index") {
    <div class="banner">Welcome!</div>
}

@IF({count} > 0) {
    <p>You have items.</p>
} @ELSE {
    <p>No items yet.</p>
}

Supported Operators

Operator Example
== @IF(#PAGE_ID == "index")
!= @IF({type} != "hidden")
< > <= >= @IF({count} >= 10)
AND @IF({loggedIn} AND {isAdmin})
OR @IF({showA} OR {showB})
NOT @IF(NOT {disabled})

Values

Bare values are evaluated for truthiness: empty string and false are falsy, everything else is truthy.

Example

src/skeleton.html:

<!DOCTYPE html>
<html>
<head>
    <title>#PAGE_HEAD_TITLE</title>
</head>
<body>
    <nav><a href="/">Home</a></nav>
    #PAGE_CONTENT
</body>
</html>

src/components/button/button.html:

#INPUT label
#INPUT href

<a class="btn" href="{href}">{label}</a>

src/pages/index.html:

#PAGE_TITLE:Home

<h1>Welcome</h1>
<c-button label="Learn More" href="/about" />

Output (dist/index.html):

<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <nav><a href="/">Home</a></nav>
    <h1>Welcome</h1>
    <a class="btn" href="/about">Learn More</a>
</body>
</html>