Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
npx --no -- commitlint --edit "$1"
7 changes: 7 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#!/bin/sh
set -e

# Lint staged CSS files
STAGED_CSS=$(git diff --cached --name-only --diff-filter=ACM | grep '\.css$' || true)
if [ -n "$STAGED_CSS" ]; then
echo "$STAGED_CSS" | tr '\n' '\0' | xargs -0 npx stylelint
fi

# Rebuild dist bundle and stage it
node scripts/bundle.js
output=$(node -e "process.stdout.write(require('./bundle.config.json').output)")
git add "$output"
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
lint:
name: Lint CSS
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run lint:css

build:
name: Build bundle
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
48 changes: 48 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Release

on:
push:
tags:
- 'v*'

jobs:
release:
name: Publish GitHub Release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- name: Extract changelog for this version
id: changelog
run: |
VERSION="${GITHUB_REF_NAME#v}"
NOTES=$(awk -v target="$VERSION" '
$1 == "##" {
heading = $2
gsub(/^\[/, "", heading)
gsub(/\]$/, "", heading)
if (found) exit
if (heading == target) { found = 1; next }
}
found { print }
' CHANGELOG.md)
if [ -z "$NOTES" ]; then
echo "Warning: no CHANGELOG section found for v$VERSION" >&2
fi
echo "notes<<EOF" >> "$GITHUB_OUTPUT"
echo "$NOTES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- uses: softprops/action-gh-release@v2
with:
Comment on lines +15 to +45

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify workflow action refs are immutable SHAs.
# Expected after fix: no output.
rg -nP '^\s*-\s*uses:\s*[^@\s]+@(?![0-9a-f]{40}\b)[^\s]+' .github/workflows/*.yml

Repository: codeslash-dev/SLASHED

Length of output: 600


Pin workflow actions to immutable commit SHAs.

Three actions in this file use mutable major version tags (@v4, @v2) instead of full commit SHAs (lines 15, 18, 32). Mutable tags allow upstream changes to alter your release pipeline without code review. Pin to commit SHAs and rotate them periodically:

  • actions/checkout@v4actions/checkout@<full-sha>
  • actions/setup-node@v4actions/setup-node@<full-sha>
  • softprops/action-gh-release@v2softprops/action-gh-release@<full-sha>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 15 - 33, The workflow uses
mutable major-version tags for three actions—actions/checkout@v4,
actions/setup-node@v4, and softprops/action-gh-release@v2—so replace each tag
with the corresponding immutable full commit SHA for that action (e.g.,
actions/checkout@<full-sha>, actions/setup-node@<full-sha>,
softprops/action-gh-release@<full-sha>) to pin behavior; locate the three
occurrences in the release.yml step list and update the version strings to the
full SHAs (you can fetch the exact SHAs from each action's GitHub repository
releases/tags) and rotate them periodically when you intentionally update the
workflow.

body: ${{ steps.changelog.outputs.notes }}
files: dist/slashed.essential.css
fail_on_unmatched_files: true
28 changes: 28 additions & 0 deletions .release-it.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "https://unpkg.com/release-it/schema/release-it.json",
"increment": "patch",
"git": {
"commitMessage": "chore(release): v${version}",
"tagName": "v${version}",
"tagAnnotation": "Release v${version}",
"push": true
},
"github": {
"release": false
},
"npm": {
"publish": false
},
"hooks": {
"before:init": ["npm run lint:css"],
"after:bump": "npm run build"
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": { "name": "angular" },
"infile": "CHANGELOG.md",
"header": "# Changelog\n",
"ignoreRecommendedBump": true
}
}
}
40 changes: 40 additions & 0 deletions .stylelintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"extends": ["stylelint-config-standard"],
"ignoreFiles": ["dist/**", "node_modules/**"],
"rules": {
"color-function-notation": "modern",
"alpha-value-notation": "number",

"custom-property-pattern": ["^sf-", {
"message": "Custom properties must start with --sf-"
}],
"keyframes-name-pattern": ["^sf-", {
"message": "Keyframe names must start with sf-"
}],
"selector-class-pattern": null,
"selector-id-pattern": null,

"function-no-unknown": [true, {
"ignoreFunctions": ["sign", "light-dark"]
}],

"lightness-notation": null,
"hue-degree-notation": null,

"number-max-precision": null,

"declaration-block-single-line-max-declarations": null,

"no-duplicate-selectors": null,
"no-descending-specificity": null,

"custom-property-empty-line-before": null,
"comment-empty-line-before": null,
"rule-empty-line-before": null,
"at-rule-empty-line-before": null,
"declaration-empty-line-before": null,

"import-notation": null,
"declaration-block-no-redundant-longhand-properties": null
}
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Changelog
74 changes: 63 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

A cascade-layer CSS framework. No build step. No Node. No runtime dependencies.

> Work in progress — this README is not final.
[![release](https://img.shields.io/github/v/release/codeslash-dev/SLASHED?label=version&color=blueviolet&logo=css3)](https://github.com/codeslash-dev/SLASHED/releases/latest)
[![CI](https://img.shields.io/github/actions/workflow/status/codeslash-dev/SLASHED/ci.yml?label=CI&logo=github)](https://github.com/codeslash-dev/SLASHED/actions/workflows/ci.yml)
[![license](https://img.shields.io/github/license/codeslash-dev/SLASHED)](LICENSE)

---

Expand All @@ -29,13 +31,15 @@ A cascade-layer CSS framework. No build step. No Node. No runtime dependencies.
<link rel="stylesheet" href="optional/tokens.components.css">
<link rel="stylesheet" href="optional/components.css">
<link rel="stylesheet" href="optional/utilities.css">
<link rel="stylesheet" href="optional/themes.css">
<link rel="stylesheet" href="optional/motion.css">

<!-- optional, load LAST -->
<link rel="stylesheet" href="optional/legacy.css">
```

Or use the pre-built bundle (see [Releases](https://github.com/codeslash-dev/SLASHED/releases/latest)):

```html
<link rel="stylesheet" href="slashed.essential.css">
```

## Cascade layer order

```text
Expand All @@ -58,17 +62,65 @@ the `slashed.tokens` layer.

| Bundle | Contents |
| --- | --- |
| `slashed.core.css` | `layers` + `tokens` + `reset` + `base` |
| `slashed.essential.css` | core + `layout` + `states` + `accessibility` + `print` |
| `slashed.full.css` | everything |
| `slashed.essential.css` | `layers` + `tokens` + `reset` + `base` + `layout` + `states` + `accessibility` + `print` |

## Customising tokens

Override the 22 source tokens in your own stylesheet — any valid CSS color works (hex, oklch, hsl…):

```css
:root {
/* light values */
--sf-color-primary-light: #3b5bdb;
--sf-color-action-light: #0ca678;
--sf-color-neutral-light: #495057;
--sf-color-base-light: #f8f9fa;

/* dark values */
--sf-color-primary-dark: #748ffc;
--sf-color-action-dark: #38d9a9;
--sf-color-neutral-dark: #adb5bd;
--sf-color-base-dark: #1a1b1e;
}
```

Or shorthand with `light-dark()`:

```css
:root {
--sf-color-primary: light-dark(#3b5bdb, #748ffc);
}
```

Core ships only as a bundle, so all its tokens live in one
`tokens.css`. `motion`, `utilities`, `components`, `themes` are
optional and combinable in any way.
## Dark mode

```html
<!-- follows OS preference by default -->
<html>

<!-- force dark -->
<html data-theme="dark">

<!-- dark section inside light page -->
<section data-theme="dark">
```

## Browser support

Targets modern browsers; requires native cascade layers (floor:
~2022 — Safari 15.4, Chrome 99, Firefox 97). `optional/legacy.css`
smooths remaining gaps inside that window but does **not** extend
support to pre-`@layer` browsers.

## Development

```sh
npm run build # bundle dist/slashed.essential.css
npm run watch # rebuild on change
npm run lint:css # stylelint all CSS
npm run lint:css:fix # auto-fix where possible
npm run release # bump version, update CHANGELOG, tag & push
```

Commits follow [Conventional Commits](https://www.conventionalcommits.org/).
`CHANGELOG.md` is generated automatically on release.
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default { extends: ['@commitlint/config-conventional'] };
Loading
Loading