kyse

package module
v0.28.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2026 License: MIT Imports: 1 Imported by: 0

README

Arandu

arandu-io/kyse

The component library kyse views import to draw a screen.

Build Status Go Reference Latest Version License

What this is

kyse names two things, and the name is overloaded on purpose. One is the template language an Arandu view is written in: a .kyse.go file, compiled to plain Go by the compiler in aru/internal/kyse — a build step, run by aru view:build, never a runtime a request passes through. The other is this repository: the library of components, written in that language, that a view imports to draw a screen.

The components themselves are adapted from shadcn/ui by way of shadcn-htmx, and importing the second is how a view writes the first.

Install

go get github.com/arandu-io/kyse/components

That is the whole installation. Nothing is copied into your project, there is no directory to keep in sync, and aru view:build has nothing extra to compile.

Use

//go:build kyse

package posts

import "github.com/arandu-io/kyse/components"

@extends('layouts.app')

@section('content')
	<form method="post" action="{{ .SubmitURL }}">
		@csrf
		{!! components.Field(components.FieldProps{
			Name:     "title",
			Label:    "Title",
			Value:    .Post.Title,
			Page:     .,
			Required: true,
		}) !!}

		{!! components.Button(components.ButtonProps{
			Label: "Publish",
			Type:  "submit",
		}) !!}
	</form>
@endsection

Every component is an ordinary exported Go function that returns template.HTML. That is the point of it: a component that does not exist is undefined: components.Buton and a prop that does not exist is unknown field Labl, both at the line of the .kyse.go you wrote. A component library resolved by string at run time would report the same two mistakes as a blank space and a 500.

What is here

group components
Actions Button ButtonGroup
Forms Field Input Label Textarea Checkbox RadioGroup Switch Select Combobox InputGroup RangeSlider
Layout Card Item Separator Sidebar
Display Avatar Badge Kbd Skeleton Table StatCard
Feedback Alert Toast Progress Empty Dialog
Overlays Drawer Popover DropdownMenu Command Collapsible
Navigation Accordion Breadcrumb Tabs ThemeToggle

Thirty-seven. What is here is what has been adapted, not what is planned.

Three that were asked for are not here, each for a reason in the tree rather than a preference. An alert dialog is a boolean on Dialog, because a component whose whole difference is derived from one decision is that decision. A tooltip is not deliverable: the stylesheet hides the bubble on keyboard focus and its text is generated content rather than a node, so it would serve a mouse and nobody else. An autosizing textarea needs a script.

Two more packages travel in the same module and neither draws a page. mailui draws the messages an application sends, in tables and inline attributes, because a stylesheet does not survive a mail client. fonts serves the faces a project vendored and writes the preload links for them.

Icons

Phosphor (MIT), duotone weight, as 1512 exported functions in github.com/arandu-io/kyse/icons:

{!! icons.Trash(icons.Props{Label: "Delete this post"}) !!}

One function per icon, rather than go:embed or a map keyed by name, because the linker can prove a function is unreachable and cannot prove that about a map lookup. An application carries the icons it calls instead of retaining all 1512 through a runtime registry. See ADR 0033.

No Label means the icon is decorative and is marked aria-hidden; a Label makes it the accessible name, which is what an icon-only button needs. There is no size and no colour prop: the svg is currentColor at 1em, and both lose to the stylesheet rule of whatever contains it.

The paths are regenerated from a pinned commit with go generate ./..., and Phosphor's licence sits in icons/LICENSE.md beside them.

The stylesheet

The markup carries semantic class names — btn, field, card — and the rules behind them ship with the skeleton, under resources/css/basecoat/. A project created with aru new already has them.

That split is deliberate. The same button written with utility classes is 443 characters of markup; written as class="btn" it is thirty. The rules exist once in a stylesheet you own and can edit, rather than once per element in every view that draws a button.

Working on a component

The sources are the .kyse.go files under components/, and the .go beside each one is compiled output that is committed — a module whose generated files are missing is a module go get cannot use.

aru view:build

Run it from the root of this module. aru view:build compiles the views of whatever module it is in: a project keeps them under resources/views, a library keeps them at its root, and the command looks rather than being told. Commit what it writes.

Where this came from

The component set, the data-variant / data-size attribute API and the HTMX patterns are adapted from shadcn-htmx (MIT). The stylesheet is Basecoat (MIT). The icons are Phosphor (MIT). Every licence travels with what was taken, and THIRD_PARTY.md says what came from where: Basecoat's notice sits beside the vendored CSS in the skeleton, Phosphor's in icons/LICENSE.md, and shadcn-htmx's in THIRD_PARTY.md, because what was taken from it is a shape rather than a file and there is no directory to put it next to. This project's own is in LICENSE.md.

Nothing arrives through npm and nothing is fetched from a CDN. There is no package.json in an Arandu project and the Content-Security-Policy is script-src 'self', so a script from another host would not run even if one were referenced.

Learning Arandu

The API reference is generated from the doc comments and lives on pkg.go.dev. Every exported symbol carries one, and that is deliberate: it is the documentation that cannot drift from the code, because it sits in the same file.

The CLI documents itself. aru help lists every command, and each one explains what it writes and what to do with it. aru doctor explains what it found and what breaks, not which rule was violated.

A guide and a website do not exist yet, and that is a decision rather than a gap: a guide written against an API that still moves is work done twice, and the second time is worse — there is wrong documentation published. The site is the next phase, and it will be an Arandu application.

Contributing

See CONTRIBUTING.md.

Security Vulnerabilities

Please review our security policy on how to report a vulnerability. Never open a public issue for one.

License

Open-sourced software licensed under the MIT license.

Documentation

Overview

Package kyse holds what a caller writes in a view and a component reads, where the two need a type and neither owns it.

It is one type today. The components live under components/, and what is here is what they take rather than what they are.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CSS

type CSS string

CSS is a block of stylesheet scoped to one component instance.

components.Card(components.CardProps{
	ComponentProps: components.ComponentProps{
		Style: kyse.CSS(`
			& { gap: 6px; }
			& [data-part="title"] { letter-spacing: -0.01em; }
		`),
	},
})

Where the block goes, and why not into the page

It does not travel in the page. The policy is style-src 'self' with no unsafe-inline, so neither a style attribute nor a style element survives, and both fail by being dropped rather than by saying anything -- the markup reads correctly and the browser ignores it.

What travels is a class. `aru view:build` reads these blocks out of the source, replaces & with the class, and writes the rules into the project's stylesheet, which is served from the origin like every other asset. The class is the hash of the block's own text, so the build and the render agree without a table between them: neither knows about the other, and both are looking at the same bytes.

The one condition

The argument has to be a string literal. A block built at run time is a hash the render can compute and the build never saw, so the rule is never emitted and the element carries a class nothing styles -- which renders, and is silent. `aru view:build` refuses a call whose argument is anything else, and names the file and the line.

It is the same limit a progress bar's width is written around, for the same reason: what the stylesheet contains is decided at build time, from the text in the source.

func (CSS) Class

func (c CSS) Class() string

Class is the class this block is compiled under, or empty when there is no block.

func (CSS) Text

func (c CSS) Text() string

Text is the block as written, which is what the build reads and hashes.

Directories

Path Synopsis
Package components draws the pieces a screen is made of.
Package components draws the pieces a screen is made of.
Package fonts serves the faces a project vendored.
Package fonts serves the faces a project vendored.
Package icons draws the Phosphor icon set, one exported function per icon.
Package icons draws the Phosphor icon set, one exported function per icon.
internal
icongen command
Command icongen vendors the Phosphor icon set into Go source.
Command icongen vendors the Phosphor icon set into Go source.
Package mailui draws the messages an application sends.
Package mailui draws the messages an application sends.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL