gcss

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2024 License: MIT Imports: 5 Imported by: 0

README

Test Go Report Card

banner

gcss

CSS written in Pure Go.

Motivation

This is really just a bit of fun and a way to write CSS in Go. I wanted to see if it was possible and what would it look like. I wanted to find a way to easily control the CSS from the server side and not have to worry about pre-building the css to take variables and stuff. I didnt want to use UI libraries that are written for JS frameworks and I didn't want to use preprocessors or linters that add more steps to the build process.

Could I just use CSS? Yes of course and I will, but I wanted to see if I could write CSS in Go as this is what is compiling the rest of the project.

Gopher

No it looks nothing like the Go gopher, but it's a gopher and I like it. It's the best I could get from the LM without giving up, ideogram.ai (1400097641).

Next steps

The next steps for this project are to add more features to the CSS package. This includes adding support for more CSS properties when the need arises. What I don't want to do is to add support for all CSS functionality as some things are better in CSS, but I do want to be able to create a few UI components that are configurable using Go.

Installation

go get github.com/AccentDesign/gcss

Quickstart

There is a separate repo with the full example here which will evolve over time.

git clone https://github.com/AccentDesign/gcss-starter.git

install the dependencies:

# for hot reloading
go install github.com/cosmtrek/air@latest
go mod tidy

run the server:

air

Usage

Basic usage

gcss defines a Style type that can be used to hold the properties for a specific css selector, eg:

style := gcss.Style{
    Selector: "body",
    Props: gcss.Props{
        BackgroundColor: props.ColorRGBA(0, 0, 0, 128),
    },
}

The CSS function on the Style is used to write the style to a io.Writer:

style.CSS(os.Stdout)

which gives you:

body{background-color:rgba(0,0,0,0.50);}

That's all there is to it. But it's not very useful on it's own I hear you say.

Multiple styles

Well you can then use that to define a Styles type that can be used to hold multiple Style types:

type Styles []gcss.Style

func (s Styles) CSS(w io.Writer) error {
    // handle your errors
    for _, style := range s {
        style.CSS(w)
    }
    return nil
}

styles := Styles{
    {
        Selector: "body",
        Props: gcss.Props{
            BackgroundColor: props.ColorRGBA(0, 0, 0, 128),
        },
    },
    {
        Selector: "main",
        Props: gcss.Props{
            Padding: props.UnitRem(8.5),
        },
    },
}

styles.CSS(os.Stdout)

which gives you:

/* formatted for visibility */
body{
    background-color:rgba(0,0,0,0.50);
}
main{
    padding:8.500rem;
}
Need a bit more? what about a dark and light theme? keep the last example in mind and read on.

Define a Theme type that can be used to hold attributes for a specific theme, eg:

type Theme struct {
    MediaQuery string
    Background props.Color
}

func (t *Theme) CSS(w io.Writer) error {
    // handle your errors
    fmt.Fprintf(w, "%s{", t.MediaQuery)
    for _, style := range t.Styles() {
        style.CSS(w)
    }
    fmt.Fprint(w, "}")
}

// Styles returns the styles for the theme.
// Can be any number of styles you want and any number of functions
// you just need them in the CSS function to loop over.
func (t *Theme) Styles() Styles {
    return Styles{
        {
            Selector: "body",
            Props: gcss.Props{
                BackgroundColor: t.Background,
            },
        },
    }
}

Then you can define a Stylesheet type that can be used to hold multiple Theme types:

type Stylesheet struct {
    Dark  *Theme
    Light *Theme
}

func (s *Stylesheet) CSS(w io.Writer) error {
    // handle your errors
    s.Dark.CSS(w)
    s.Light.CSS(w)
    return nil
}

Finally, you can use the Stylesheet type to write the css to a io.Writer:

styles := Stylesheet{
    Dark: &Theme{
        MediaQuery: "@media (prefers-color-scheme: dark)",
        Background: props.ColorRGBA(0, 0, 0, 255),
    },
    Light: &Theme{
        MediaQuery: "@media (prefers-color-scheme: light)",
        Background: props.ColorRGBA(255, 255, 255, 255),
    },
}

styles.CSS(os.Stdout)

gives you:

/* formatted for visibility */
@media (prefers-color-scheme: dark) {
    body{
        background-color:rgba(0,0,0,1.00);
    }
}
@media (prefers-color-scheme: light) {
    body{
        background-color:rgba(255,255,255,1.00);
    }
}

Hopefully this will get you going. The rest is up to you.

  • Maybe create a button function that takes a props.Color and returns a Style.
  • Or add extra Styles to the Stylesheet to additionally include non themed styles.
  • It's all about how you construct the Stylesheet and use the gcss.Style type.
  • If I could have created a Stylesheet type that fits well any use case at all I would have, but there is a world of possibility, so I left it up to you.

The benefits

  • Total control of the CSS from the server side.
  • CSS doesn't have mixins, but you can create a function that returns a Style type and reuse it.
  • Keeps the css free of variables.
  • Keeps html free of classes like bg-gray-50 text-black dark:bg-slate-800 dark:text-white and eliminates the need to remember to add the dark variant.
  • I recently saw a button component on an html page 10 times with over 1800 characters in the class attribute of each. This is not maintainable nor debuggable.
  • Keeps the css clean and easy to debug with no overrides like the above.

Examples

For example usage see the examples directory that include:

Contributing

If you would like to contribute to this project, please open an issue or a pull request. We welcome all contributions and ideas.

Mix it up with other CSS frameworks

You can mix gcss with other CSS frameworks like tailwindcss for example:

separate the css files into base and utils:

/* base.css */
@tailwind base;
/* utils.css */
@tailwind utilities;

Then add the gcss styles in between in your html:

<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="gcss-styles.css">
<link rel="stylesheet" href="utils.css">

Try to keep the specificity of the gcss styles to 1 by using single classes this will ensure any tailwindcss utilities will be able to overwrite your styles where required.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CustomProp added in v0.0.5

type CustomProp struct {
	Attr, Value string
}

CustomProp represents an additional CSS property that is not covered by the Props struct.

type Props

type Props struct {
	AlignContent            props.AlignContent        `css:"align-content"`
	AlignItems              props.AlignItems          `css:"align-items"`
	AlignSelf               props.AlignSelf           `css:"align-self"`
	Appearance              props.Appearance          `css:"appearance"`
	BackgroundColor         props.Color               `css:"background-color"`
	BackgroundImage         props.BackgroundImage     `css:"background-image"`
	BackgroundPosition      props.BackgroundPosition  `css:"background-position"`
	BackgroundRepeat        props.BackgroundRepeat    `css:"background-repeat"`
	BackgroundSize          props.BackgroundSize      `css:"background-size"`
	Border                  props.Border              `css:"border"`
	BorderBottom            props.Border              `css:"border-bottom"`
	BorderBottomLeftRadius  props.Unit                `css:"border-bottom-left-radius"`
	BorderBottomRightRadius props.Unit                `css:"border-bottom-right-radius"`
	BorderCollapse          props.BorderCollapse      `css:"border-collapse"`
	BorderColor             props.Color               `css:"border-color"`
	BorderLeft              props.Border              `css:"border-left"`
	BorderRadius            props.Unit                `css:"border-radius"`
	BorderRight             props.Border              `css:"border-right"`
	BorderStyle             props.BorderStyle         `css:"border-style"`
	BorderTop               props.Border              `css:"border-top"`
	BorderTopLeftRadius     props.Unit                `css:"border-top-left-radius"`
	BorderTopRightRadius    props.Unit                `css:"border-top-right-radius"`
	BorderWidth             props.Unit                `css:"border-width"`
	Bottom                  props.Unit                `css:"bottom"`
	BoxSizing               props.BoxSizing           `css:"box-sizing"`
	CaptionSide             props.CaptionSide         `css:"caption-side"`
	Color                   props.Color               `css:"color"`
	ColumnGap               props.Unit                `css:"column-gap"`
	Cursor                  props.Cursor              `css:"cursor"`
	Display                 props.Display             `css:"display"`
	FlexBasis               props.Unit                `css:"flex-basis"`
	FlexDirection           props.FlexDirection       `css:"flex-direction"`
	FlexGrow                props.Unit                `css:"flex-grow"`
	FlexShrink              props.Unit                `css:"flex-shrink"`
	FlexWrap                props.FlexWrap            `css:"flex-wrap"`
	Float                   props.Float               `css:"float"`
	FontFamily              props.FontFamily          `css:"font-family"`
	FontSize                props.Unit                `css:"font-size"`
	FontStyle               props.FontStyle           `css:"font-style"`
	FontWeight              props.FontWeight          `css:"font-weight"`
	Gap                     props.Unit                `css:"gap"`
	Height                  props.Unit                `css:"height"`
	JustifyContent          props.JustifyContent      `css:"justify-content"`
	JustifyItems            props.JustifyItems        `css:"justify-items"`
	JustifySelf             props.JustifySelf         `css:"justify-self"`
	Left                    props.Unit                `css:"left"`
	LineHeight              props.Unit                `css:"line-height"`
	ListStylePosition       props.ListStylePosition   `css:"list-style-position"`
	ListStyleType           props.ListStyleType       `css:"list-style-type"`
	Margin                  props.Unit                `css:"margin"`
	MarginBottom            props.Unit                `css:"margin-bottom"`
	MarginLeft              props.Unit                `css:"margin-left"`
	MarginRight             props.Unit                `css:"margin-right"`
	MarginTop               props.Unit                `css:"margin-top"`
	MaxHeight               props.Unit                `css:"max-height"`
	MaxWidth                props.Unit                `css:"max-width"`
	MinHeight               props.Unit                `css:"min-height"`
	MinWidth                props.Unit                `css:"min-width"`
	Opacity                 props.Unit                `css:"opacity"`
	Overflow                props.Overflow            `css:"overflow"`
	OverflowX               props.Overflow            `css:"overflow-x"`
	OverflowY               props.Overflow            `css:"overflow-y"`
	Padding                 props.Unit                `css:"padding"`
	PaddingBottom           props.Unit                `css:"padding-bottom"`
	PaddingLeft             props.Unit                `css:"padding-left"`
	PaddingRight            props.Unit                `css:"padding-right"`
	PaddingTop              props.Unit                `css:"padding-top"`
	Position                props.Position            `css:"position"`
	PrintColorAdjust        props.PrintColorAdjust    `css:"print-color-adjust"`
	Right                   props.Unit                `css:"right"`
	RowGap                  props.Unit                `css:"row-gap"`
	TextAlign               props.TextAlign           `css:"text-align"`
	TextDecorationColor     props.Color               `css:"text-decoration-color"`
	TextDecorationLine      props.TextDecorationLine  `css:"text-decoration-line"`
	TextDecorationStyle     props.TextDecorationStyle `css:"text-decoration-style"`
	TextDecorationThickness props.Unit                `css:"text-decoration-thickness"`
	TextIndent              props.Unit                `css:"text-indent"`
	TextOverflow            props.TextOverflow        `css:"text-overflow"`
	TextTransform           props.TextTransform       `css:"text-transform"`
	TextUnderlineOffset     props.Unit                `css:"text-underline-offset"`
	TextWrap                props.TextWrap            `css:"text-wrap"`
	Top                     props.Unit                `css:"top"`
	VerticalAlign           props.VerticalAlign       `css:"vertical-align"`
	WhiteSpace              props.WhiteSpace          `css:"white-space"`
	Width                   props.Unit                `css:"width"`
	Visibility              props.Visibility          `css:"visibility"`
	ZIndex                  props.Unit                `css:"z-index"`
}

Props represents the standard CSS properties that can be applied to a CSS rule.

func (*Props) CSS added in v0.1.2

func (p *Props) CSS(w io.Writer) error

CSS writes the CSS representation of the props to the writer.

type Style

type Style struct {
	// Selector is the CSS selector to which the properties will be applied.
	// It can be any valid CSS selector like class, id, element type, etc.
	Selector string

	// Props contains the standard CSS properties that will be applied to the selector.
	// These properties are represented by the Props struct and are strongly typed.
	Props Props

	// CustomProps contains any additional CSS properties that are not covered by the Props struct.
	// These properties are directly added to the CSS rule as is.
	CustomProps []CustomProp
}

Style represents a CSS style rule.

func (*Style) CSS

func (s *Style) CSS(w io.Writer) error

CSS writes the CSS representation of the style to the writer.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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