elem-go

repository·main·Indexed 18 days ago

https://github.com/chasefleming/elem-go

A type-safe HTML construction library for Go that allows developers to build complex HTML views using pure Go code instead of templates. It provides compile-time safety for elements and attributes, featuring an `attrs` subpackage for common HTML attributes, a `styles` package for managing CSS and responsive design, and a dedicated `htmx` subpackage providing constants for htmx 2.x attributes to enable asynchronous updates and partial page refreshes.

Tokens
13.2K
Snippets
55
Records
61
Agent score
61%

What's inside elem-go

  1. Understand the Todo List application structure and routes

    main

    The application uses Go Fiber for routing and elem-go for server-side HTML generation. The following routes are defined:

    • GET /: Displays the initial list of Todo items.
    • POST /toggle/:id: Toggles the completion status of a specific Todo item based on its ID.
    • POST /add: Processes a new Todo item addition.

    Asynchronous updates (adding items or toggling status) are handled via htmx to avoid full page reloads.

  2. Understand the htmx-fiber-counter architecture

    main

    This example demonstrates a full-stack pattern using three technologies:

    • Go Fiber: Acts as the web server and routing engine.
    • elem-go: Used on the server side to programmatically construct HTML elements.
    • htmx: Handles dynamic front-end interactions by making AJAX requests (via hx-post and hx-target) to specific endpoints, allowing partial page updates without a full refresh.

    Route Definitions

    • GET /: Serves the initial HTML page constructed with elem-go. This page includes the htmx script and the UI components.
    • POST /increment: An endpoint that increases the counter state and returns the new value as a string.
    • POST /decrement: An endpoint that decreases the counter state and returns the new value as a string.
  3. Understand the htmx-counter application architecture

    main

    The htmx-counter application demonstrates how to combine elem-go for programmatic HTML generation with htmx for asynchronous updates.

    API Endpoints

    • GET /: Renders the initial home page containing the current counter value and an increment button.
    • POST /increment: An endpoint that increments the counter state and returns the updated HTML fragment.

    Interaction Pattern

    Instead of full page reloads, the application uses htmx attributes applied to elem-go elements. When the increment button is clicked, htmx sends a POST request to /increment. The server responds with the new counter value (as an HTML fragment), and htmx swaps the relevant part of the DOM automatically.

  4. Use StyleManager for advanced CSS styling in Go

    main

    The StyleManager (located in the styles package) allows you to manage CSS styles directly within your Go code. It is designed for creating dynamic and responsive web interfaces without leaving the Go environment.

    Key capabilities include:

    • Pseudo-classes: Implementing effects like :hover for visual feedback (e.g., changing color or scale).
    • Animations: Defining and applying CSS animations to elements.
    • Responsive Design: Using media queries to adjust styles based on viewport width (e.g., changing background colors dynamically).
  5. Perform conditional rendering with `If` and `None`

    main

    Conditional rendering is handled by the elem.If utility.

    • elem.If(condition, trueNode, falseNode): Renders trueNode if the condition is met, otherwise falseNode.
    • elem.None(): A specialized node that implements the Node interface but produces no output. Use this as the falseNode in elem.If when you want to render nothing if a condition is false.
    • Empty Elements: elem.None() can also be used to create empty tags, e.g., elem.Div(nil, elem.None()) results in <div></div>.
    // Conditional rendering
    content := elem.Div(nil,
        elem.H1(nil, elem.Text("Dashboard")),
        elem.If(isAdmin, adminLink, guestLink),
    )
    
    // Rendering nothing if false
    content := elem.Div(nil,
        elem.If[elem.Node](showWelcomeMessage, welcomeMessage, elem.None()),
    )
  6. Advanced styling with `StyleManager`

    main

    The StyleManager provides a structured way to handle complex CSS features like pseudo-classes (e.g., :hover), animations, and media queries directly in Go. It automatically handles class name generation and style deduplication.

    To use StyleManager:

    1. Initialize it with styles.NewStyleManager().
    2. Define a styles.CompositeStyle containing Default props and a map of PseudoClasses.
    3. Apply the generated class name to an element.
    4. Crucially, render the element using RenderWithOptions and pass the StyleManager instance in the elem.RenderOptions.
    // Initialize StyleManager
    styleMgr := styles.NewStyleManager()
    
    // Define styles with a hover effect
    buttonClass := styleMgr.AddCompositeStyle(styles.CompositeStyle{
        Default: styles.Props{
            styles.BackgroundColor: "green",
            styles.Color:           "white",
            styles.Padding:         "10px 20px",
            styles.Border:          "none",
            styles.Cursor:          "pointer",
        },
        PseudoClasses: map[string]styles.Props{
            styles.PseudoHover: {
                styles.BackgroundColor: "darkgreen",
            },
        },
    })
    
    // Create a button and apply the generated class name
    button := elem.Button(
        attrs.Props{attrs.Class: buttonClass},
        elem.Text("Hover Over Me"),
    )
    
    // Use RenderWithOptions to apply the style definitions effectively
    htmlOutput := button.RenderWithOptions(elem.RenderOptions{StyleManager: styleMgr})
  7. Style elements using `styles.Props`

    main

    The styles.Props type allows you to define CSS properties in a structured, type-safe manner. Instead of using raw string literals for property names, use the provided constants (e.g., styles.BackgroundColor) to prevent typos.

    To apply these styles to an HTML element, use the ToInline() method to convert the Props object into a CSS string, then pass it to the element's style attribute.

    // Define styles using styles.Props and constants
    buttonStyle := styles.Props{
        styles.BackgroundColor: "#4CAF50",
        styles.Border:          "none",
    }
    
    // Convert styles to inline CSS
    inlineStyle := buttonStyle.ToInline()
    
    // Apply inline CSS to a button element
    button := elem.Button(attrs.Props{attrs.Style: inlineStyle}, elem.Text("Click Me"))
  8. Run the htmx-fiber-counter example

    main

    To run the counter application demo, ensure you have Go installed, then follow these steps:

    1. Install dependencies: Run go mod tidy to download elem-go, Go Fiber, and the required htmx subpackages.
    2. Start the server: Run go run main.go. The server will start on http://localhost:3000.
    3. Interact: Open your browser to the local URL. You can click '+' or '-' to trigger htmx requests to the /increment and /decrement endpoints.
    go mod tidy
    go run main.go
  9. Handle JSON strings in attributes

    main

    When setting attributes that contain JSON strings or special characters (like quotes), wrap the string value in single quotes. This prevents the library from adding extra quotes around the value and ensures valid HTML.

    content := elem.Div(attrs.Props{
        attrs.ID:    "my-div",
        attrs.Class: "special 'class'",
        attrs.Data:  `'{"key": "value"}'`,
    }, elem.Text("Content"))
  10. Handle JSON strings in htmx attributes

    main

    When using attributes that require JSON strings (like hx-vals), you must wrap the JSON string in single quotes to ensure correct rendering and parsing by htmx.

    content := elem.Div(attrs.Props{
        htmx.HXGet:  "/example",
        htmx.HXVals: `'{"myVal": "My Value"}'`,
    }, elem.Text("Get Some HTML, Including A Value in the Request"))