Type-safe Go library for creating and manipulating HTML elements (with htmx helpers).
MIT License
Fragment
for Grouping ElementsYou can now use the Fragment
function to group multiple HTML elements together without adding an extra wrapper element to the DOM. This is particularly useful when you want to merge nodes into a single parent without adding unnecessary structure.
Below is an example of how you can utilize the Fragment
function in your Go code:
import (
"github.com/chasefleming/elem-go"
)
func main() {
nodes1 := []elem.Node{
elem.P(nil, elem.Text("1")),
elem.P(nil, elem.Text("2")),
}
nodes2 := []elem.Node{
elem.P(nil, elem.Text("3")),
elem.P(nil, elem.Text("4")),
}
content := elem.Div(nil,
elem.P(nil, elem.Text("0")),
elem.Fragment(nodes1...),
elem.Fragment(nodes2...),
)
html := content.Render()
fmt.Println(html)
}
This code will produce the following HTML output:
<div><p>0</p><p>1</p><p>2</p><p>3</p><p>4</p></div>
Published by chasefleming 3 months ago
You can now define attributes with values enclosed in single quotes, avoiding the need for additional escaping when double quotes are used within the attribute value itself.
Below is an example of how you can utilize single-quoted attribute values in your Go code:
import (
"github.com/chasefleming/elem-go"
"github.com/chasefleming/elem-go/attrs"
)
func main() {
content := elem.Div(attrs.Props{
"data-values": `'{"quantity": 5}'`, // Single-quoted attribute value
})
html := content.Render()
fmt.Println(html)
}
This code will produce the following HTML output:
<div data-values='{"quantity": 5}'></div>
Published by chasefleming 5 months ago
Seconds
and Milliseconds
FunctionsSeconds(value float64) string
This function returns a string representation of the given time duration in seconds.
secondsValue := styles.Seconds(2.5) // Returns "2.5s"
Milliseconds(value int) string
This function returns a string representation of the given time duration in milliseconds.
millisecondsValue := styles.Milliseconds(500) // Returns "500ms"
HSL
and HSLA
FunctionsHSL(h, s, l int) string
This function returns a string representation of the given HSL color.
hslColor := styles.HSL(120, 100, 50) // Returns "hsl(120, 100%, 50%)"
HSLA(h, s, l int, a float64) string
This function returns a string representation of the given HSLA color.
hslaColor := styles.HSLA(120, 100, 50, 0.5) // Returns "hsla(120, 100%, 50%, 0.5)"
New constant, FontVariant
for the font-variant
CSS property:
textStyle := styles.Props{
styles.FontVariant: "small-caps",
}
Published by chasefleming 7 months ago
StyleManager
in elem-go
The latest addition to the elem-go
library is here: the StyleManager
. This powerful feature enhances the management of CSS styles programmatically with advanced capabilities such as pseudo-classes, animations, and media queries, directly within the Go programming environment. StyleManager
is designed to streamline the creation of dynamic and responsive web applications, empowering developers with more control and flexibility over their styling strategies.
StyleManager
FeaturesTo incorporate StyleManager
into your projects, make sure to have the latest version of elem-go
:
go get -u github.com/chasefleming/elem-go
Then, import the styles
package alongside elem-go
in your project:
import (
"github.com/chasefleming/elem-go"
"github.com/chasefleming/elem-go/styles"
)
StyleManager
Initialize StyleManager
to start creating your styles:
styleMgr := styles.NewStyleManager()
Easily apply dynamic hover effects:
buttonClass := styleMgr.AddCompositeStyle(styles.CompositeStyle{
Default: styles.Props{
styles.BackgroundColor: "blue",
styles.Color: "white",
},
PseudoClasses: map[string]styles.Props{
"hover": {styles.BackgroundColor: "darkblue"},
},
})
Bring elements to life with custom keyframe animations:
animationName := styleMgr.AddAnimation(styles.Keyframes{
"0%": {styles.Opacity: "0"},
"100%": {styles.Opacity: "1"},
})
fadeInClass := styleMgr.AddStyle(styles.Props{
styles.AnimationName: animationName,
styles.AnimationDuration: "2s",
})
Adapt your styles to different screen sizes:
responsiveClass := styleMgr.AddCompositeStyle(styles.CompositeStyle{
Default: styles.Props{styles.Display: "block"},
MediaQueries: map[string]styles.Props{
"@media (max-width: 600px)": {styles.Display: "none"},
},
})
elem-go
Directly integrate your styles with elem-go
elements:
html := elem.Div(
attrs.Props{attrs.Class: responsiveClass},
elem.Text("Responsive Text"),
)
html.RenderWithOptions(elem.RenderOptions{StyleManager: styleMgr})
StyleManager
Explore the documentation and the example application to see StyleManager
in action. StyleManager
opens new possibilities for elem-go
projects by offering sophisticated styling that aligns with Go's philosophy of simplicity, efficiency, and reliability.
Discover the dynamic and responsive web applications you can create with StyleManager
. Happy coding!
Published by chasefleming 7 months ago
This update introduces a suite of new functions to the styles
sub-package aimed at providing a type-safe approach to generating CSS style strings.
Em(value float64) string
and Rem(value float64) string
These functions return a string representation of the given value with the "em" and "rem" units, respectively.
emValue := styles.Em(2.5) // Returns "2.5em"
remValue := styles.Rem(1.5) // Returns "1.5rem"
Pixels(value int) string
This function returns a string representation of the given value with the "px" unit.
pxValue := styles.Pixels(10) // Returns "10px"
Percent(value int) string
This function returns a string representation of the given value with the "%" unit.
percentValue := styles.Percent(50) // Returns "50%"
ViewportHeight(value int) string
and ViewportWidth(value int) string
These functions return a string representation of the given value with the "vh" and "vw" units, respectively.
vhValue := styles.ViewportHeight(50) // Returns "50vh"
vwValue := styles.ViewportWidth(25) // Returns "25vw"
ViewportMin(value int) string
and ViewportMax(value int) string
These functions return a string representation of the given value with the "vmin" and "vmax" units, respectively.
vminValue := styles.ViewportMin(10) // Returns "10vmin"
vmaxValue := styles.ViewportMax(20) // Returns "20vmax"
RGB(r, g, b int) string
This function returns a string representation of the given RGB color.
rgbColor := styles.RGB(255, 0, 0) // Returns "rgb(255, 0, 0)"
RGBA(r, g, b int, a float64) string
This function returns a string representation of the given RGBA color.
rgbaColor := styles.RGBA(255, 0, 0, 0.5) // Returns "rgba(255, 0, 0, 0.5)"
Int(value int) string
This function returns a string representation of the given integer value.
intValue := styles.Int(100) // Returns "100"
Float(value float64) string
This function returns a string representation of the given float value.
floatValue := styles.Float(3.14) // Returns "3.14"
URL(url string) string
This function returns a string representation as a formatted CSS URL.
urlValue := styles.URL("https://example.com/image.jpg") // Returns "url('https://example.com/image.jpg')"
Var(name string) string
This function returns a string representation as a CSS variable.
varValue := styles.Var("primary-color") // Returns "var(--primary-color)"
Published by chasefleming 8 months ago
Merge
Added to attrs
SubpackageThe Merge
method allows for the merging of multiple attrs.Props
maps into a single attrs.Props
map. This is particularly useful when you need to apply a base set of attributes and then selectively override or add additional attributes under certain conditions. The method accepts a variadic input, enabling the easy combination of any number of attrs.Props
maps. Attributes from later arguments will override those from earlier ones if there are any key conflicts.
defaultButtonAttrs := attrs.Props{
attrs.Class: "btn",
attrs.Type: "button",
}
userButtonAttrs := attrs.Props{
attrs.Class: "btn btn-primary",
attrs.ID: "submitBtn",
}
mergedButtonAttrs := attrs.Merge(defaultButtonAttrs, userButtonAttrs)
button := elem.Button(mergedButtonAttrs, elem.Text("Submit"))
Published by chasefleming 8 months ago
<base>
ElementThe Base
function for creating <base>
elements has been added. The <base>
element specifies the base URL to use for all relative URLs in a document.
head := elem.Head(nil, elem.Base(attrs.Props{"href": "https://www.example.com/"}))
styles.CSS
to elem.CSS
To remove a circular dependency, the previously used styles.CSS
method has been moved and renamed to elem.CSS
. Projects that previously relied on styles.CSS
will need to update their references to the new elem.CSS
.
This will also fix a bug where styles.CSS
no longer implemented the Node
interface.
Published by chasefleming 8 months ago
We've enhanced elem-go
with new semantic HTML elements for accessibility. The update includes:
elem.Ruby(...)
elem.Rt(...)
elem.Rp(...)
elem.Small(...)
elem.Q(...)
elem.Cite(...)
elem.Abbr(...)
elem.Data(...)
elem.Time(...)
elem.Var(...)
elem.Samp(...)
elem.Kbd(...)
elem.Sub(...)
elem.Sup(...)
elem.B(...)
elem.U(...)
These additions enable more precise content structuring and semantic clarity, covering a range of elements from annotations and text modifications to data representation and emphasis.
Published by chasefleming 9 months ago
New htmx constants: Added extensive support for htmx attributes and hx-on::event
attributes using a --
syntax for universal compatibility. This update more closely aligns elem-go
with htmx version 1.9.10, enabling developers to implement dynamic web functionalities with improved ease and safety.
New Attribute Constants: Introduced new attribute constants for integrity
and crossorigin
for script elements, enhancing security and resource sharing capabilities in web applications.
Style Constant for Object Fit: Added a style constant for object-fit
property, allowing for more control over the visual rendering of images and videos, ensuring they fit their containers in a visually appealing manner.
Published by chasefleming 9 months ago
<h4>
, <h5>
, <h6>
, and <hgroup>
ElementsSupport for the <h4>
, <h5>
, <h6>
, and <hgroup>
HTML elements has been added, broadening the scope for semantic text structuring and document outline organization.
Create a section with nested headings using elem.H4()
, elem.H5()
, and elem.H6()
:
sectionContent := elem.Div(nil,
elem.H4(nil, elem.Text("Section Heading Level 4")),
elem.H5(nil, elem.Text("Subsection Heading Level 5")),
elem.H6(nil, elem.Text("Subsubsection Heading Level 6")),
)
This will output:
<div>
<h4>Section Heading Level 4</h4>
<h5>Subsection Heading Level 5</h5>
<h6>Subsubsection Heading Level 6</h6>
</div>
Group headings together semantically using elem.Hgroup()
:
headingGroup := elem.Hgroup(nil,
elem.H1(nil, elem.Text("Main Title")),
elem.H2(nil, elem.Text("Subtitle")),
)
This will output:
<hgroup>
<h1>Main Title</h1>
<h2>Subtitle</h2>
</hgroup>
Published by chasefleming 9 months ago
<map>
and <area>
Element SupportSupport for the <map>
and <area>
HTML elements has been added, enhancing the capability of creating interactive image maps.
Create a <map>
element with elem.Map()
and define clickable areas using elem.Area()
:
imageMap := elem.Map(attrs.Props{attrs.Name: "map1"},
elem.Area(attrs.Props{
attrs.Alt: "Area 1",
attrs.Coords: "34,44,270,350",
attrs.Href: "#area1",
attrs.Shape: "rect",
}),
)
This will output:
<map name="map1">
<area alt="Area 1" coords="34,44,270,350" href="#area1" shape="rect">
</map>
"shape"
, "coords"
, and "usemap"
have been added to the attributes subpackage.backdrop-filter"
is now available in the styles package.utils.CopyString
for optimized memory management in line with Fiber's performance strategy.Published by chasefleming 10 months ago
<optgroup>
ElementThe Optgroup
function for creating <optgroup>
elements has been added. This update allows for better organization in forms with numerous options by grouping <option>
elements within <select>
dropdowns.
el := Select(attrs.Props{attrs.Name: "cars"},
Optgroup(attrs.Props{attrs.Label: "Swedish Cars"},
Option(attrs.Props{attrs.Value: "volvo"}, Text("Volvo")),
Option(attrs.Props{attrs.Value: "saab"}, Text("Saab"))),
Optgroup(attrs.Props{attrs.Label: "German Cars"},
Option(attrs.Props{attrs.Value: "mercedes"}, Text("Mercedes")),
Option(attrs.Props{attrs.Value: "audi"}, Text("Audi")))
)
This will render the following HTML:
<select name="cars">
<optgroup label="Swedish Cars">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
</optgroup>
<optgroup label="German Cars">
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</optgroup>
</select>
elem.Html
elem.Html
now automatically includes the <!DOCTYPE html>
preamble at the beginning of the HTML document to ensure compliance with modern web standards and prevent rendering in legacy/quirks mode.
RenderWithOptions
Method for Custom Render OptionsThe new RenderWithOptions
method has been introduced to provide more flexibility in rendering. For instance, if you need to disable the automatic HTML preamble (now included by default in elem.Html
), you can do so using this method. This method takes a RenderOptions
struct with a boolean option for DisableHtmlPreamble
.
options := RenderOptions{DisableHtmlPreamble: true}
htmlString := myHtmlElement.RenderWithOptions(options)
The public NewElement
function has been made private and renamed to newElement
. Users relying on this function directly in their code will need to update their usage accordingly. If you require additional elements not currently available in the repository, please create an issue to request them.
Published by chasefleming 11 months ago
elem.None
for Conditional Rendering and Empty ElementsNone
now simplifies cases where conditional rendering requires either an element or no output at all. The function provides a straightforward way to express the absence of an element without compromising the readability or structure of your code.
In conditional rendering, None
serves as an ideal placeholder for cases where no output is required. For example, when using None
in conjunction with the If
function, it allows for cleaner and more expressive code. Consider a scenario where you need to render a welcome message conditionally:
showWelcomeMessage := false
welcomeMessage := elem.Div(nil, elem.Text("Welcome to our website!"))
content := elem.Div(nil,
elem.If[elem.Node](showWelcomeMessage, welcomeMessage, elem.None())
)
Here, welcomeMessage
is only rendered if showWelcomeMessage
is true
. Otherwise, None
ensures that there is no output.
None
also makes it straightforward to create explicitly empty elements like <div></div>
, useful in various layout and design contexts:
emptyDiv := elem.Div(nil, elem.None())
// Outputs: <div></div>
Published by chasefleming 11 months ago
Raw
HTML InsertionThe Raw
function has been added to allow direct insertion of raw HTML content into the document structure. This function is particularly useful for rendering HTML strings as part of the final output, without any modification or parsing. It offers a convenient way to integrate HTML content generated outside the elem-go
environment.
rawHTML := `<div class="custom-html"><p>Custom HTML content</p></div>`
content := elem.Div(nil,
elem.H1(nil, elem.Text("Hello, World!")),
elem.Raw(rawHTML), // Inserting the raw HTML
elem.P(nil, elem.Text("More content here...")),
)
htmlOutput := content.Render()
NOTE: Users should ensure to sanitize any HTML content from untrusted sources before using the
Raw
function to prevent security risks like XSS (Cross-Site Scripting) attacks.
Published by chasefleming 11 months ago
DataAttr
for Custom Data AttributesThis new function enables the addition of custom data attributes to HTML elements.
Div(attrs.Props{attrs.DataAttr("theme"): "cupcake"}, Text("foobar"))
// <div data-theme="cupcake">foobar</div>
A comprehensive set of ARIA (Accessible Rich Internet Applications) attributes has been added to the attrs subpackage. For a full list of the new ARIA attributes, see the constants in the attrs subpackage.
Support for step
and minlength
has been added to our attributes constants.You can access these from the attrs
subpackage as attrs.Step
and attrs.Minlength
.
attrs
PackageSeveral constants have been renamed to adhere to Go's naming conventions.
AllowFullScreen
→ AllowFullscreen
ReferrerPolicy
→ Referrerpolicy
NoValidate
→ Novalidate
MaxLength
→ Maxlength
DateTime
→ Datetime
CrossOrigin
→ Crossorigin
SrcDoc
→ Srcdoc
IsMap
→ Ismap
Users are advised to update their code to the new standards. The deprecated constants will remain available for a transition period, but will be removed in future releases.
Published by chasefleming 12 months ago
This new utility allows you to easily add HTML comments into your generated HTML.
comment := elem.Comment("Section: Main Content Start")
// Generates: <!-- Section: Main Content Start -->
<iframe>
SupportYou can now integrate <iframe>
into your HTML with the new IFrame
function.
iframe := elem.IFrame(attrs.Props{
attrs.Src: "https://www.youtube.com/embed/446E-r0rXHI",
attrs.AllowFullScreen: "true",
})
// Generates <iframe allowfullscreen src="https://www.youtube.com/embed/446E-r0rXHI"></iframe>
<audio>
and <video>
ElementsEnhance your HTML with multimedia content using our new Audio
and Video
functions.
audio := elem.Audio(attrs.Props{attrs.Controls: "true"},
elem.Source(attrs.Props{attrs.Src: "audio.mp3", attrs.Type: "audio/mpeg"}),
elem.Source(attrs.Props{attrs.Src: "audio.ogg", attrs.Type: "audio/ogg"}),
elem.Text("Your browser does not support the audio tag."),
)
// Generates <audio controls><source src="audio.mp3" type="audio/mpeg"><source src="audio.ogg" type="audio/ogg">Your browser does not support the audio tag.</audio>
video := elem.Video(attrs.Props{attrs.Width: "320", attrs.Height: "240", attrs.Controls: "true"},
elem.Source(attrs.Props{attrs.Src: "movie.mp4", attrs.Type: "video/mp4"}),
elem.Source(attrs.Props{attrs.Src: "movie.ogg", attrs.Type: "video/ogg"}),
elem.Text("Your browser does not support the video tag."),
)
// Generates <video controls height="240" width="320"><source src="movie.mp4" type="video/mp4"><source src="movie.ogg" type="video/ogg">Your browser does not support the video tag.</video>
Published by chasefleming 12 months ago
Merge
Function for styles.Props
Developers can now combine multiple style props objects into one with the new Merge
method. This feature is particularly useful for applying conditional styles on top of base styles, depending on state or other factors.
// Example style definitions
baseButtonStyle := styles.Props{
Padding: "10px 15px",
Border: "none",
FontWeight: "bold",
}
primaryStyles := styles.Props{
BackgroundColor: "blue",
Color: "white",
}
secondaryStyles := styles.Props{
BackgroundColor: "red",
Color: "white",
}
// Merging styles with the new Merge function
primaryButtonStyles := styles.Merge(baseButtonStyle, primaryStyles)
secondaryButtonStyles := styles.Merge(baseButtonStyle, secondaryStyles)
In the Merge
function, later style objects take precedence over earlier ones for properties defined in multiple style objects. This allows for fine-grained control over the styling precedence.
Style
and CSS
Functions for Embedding CSSTwo new functions, Style
and CSS
, have been introduced to streamline the creation of <style>
tags for embedding raw CSS into HTML documents.
Style
Function
The Style
function creates an *Element
struct representing a <style>
tag.
// Defining raw CSS content
cssContent := `
body { background-color: #f0f0f0; }
h1 { color: #333; }
`
// Creating a <style> tag with the CSS content
styleTag := elem.Style(nil, styles.CSS(cssContent))
// Creating an HTML document with the <style> tag included in the <head>
document := elem.Html(nil,
elem.Head(nil,
styleTag,
),
// ... other body elements
)
The CSS
function within the Style
tag helper ensures that CSS is correctly embedded and applied within the HTML document.
Published by chasefleming 12 months ago
This update introduces refactoring to the handling of attributes and styles within the elem-go
library to resolve naming conflicts and improve the overall structure.
Below are the breaking changes and examples of how to update your code accordingly:
elem.Attrs
to attrs.Props
To avoid naming conflicts with the HTML attribute names and provide a clearer distinction, elem.Attrs
has been renamed to attrs.Props
. This change emphasizes that these are properties for elements rather than direct attributes.
Old Usage:
div := elem.Div(elem.Attrs{
attrs.ID: "content",
attrs.Class: "main-content",
})
New Usage:
div := elem.Div(attrs.Props{
attrs.ID: "content",
attrs.Class: "main-content",
})
Action Required: Replace all instances of elem.Attrs
with attrs.Props
.
elem.Style
to styles.Props
To differentiate style properties from other attributes and ensure they are treated distinctly within the library, elem.Style
has been refactored to styles.Props
. This allows for a more modular approach to styling elements.
Old Usage:
style := elem.Style{
styles.BackgroundColor: "#fff",
styles.Color: "#000",
}
New Usage:
style := styles.Props{
styles.BackgroundColor: "#fff",
styles.Color: "#000",
}
Action Required: Replace all instances of elem.Style
with styles.Props
.
ApplyStyle
and Introduction of ToInline
The ApplyStyle
method, which was used to set style attributes directly on elements, has been deprecated to promote a more streamlined API. We are introducing the ToInline
method for the styles.Props
type to convert style properties to inline CSS strings more explicitly.
Old Usage with ApplyStyle
:
divStyle := elem.Style{
styles.BackgroundColor: "#f4f4f4",
// ...other styles
}
div := elem.Div(elem.Attrs{
attrs.Style: elem.ApplyStyle(divStyle),
})
New Usage with ToInline
:
divStyle := elem.Style{
styles.BackgroundColor: "#f4f4f4",
// ...other styles
}
div := elem.Div(styles.Props{
attrs.Style: divStyle.ToInline(),
})
Action Required: Migrate all usages of ApplyStyle
to use the ToInline
method on styles.Props
and set the resulting string with the attrs.Style
key when defining element attributes.
Published by chasefleming 12 months ago
With this update, developers can now easily implement a wider range of elements that cover form, interactive, and script-supporting functionalities, enhancing the interactivity and compliance of web applications with HTML standards.
Enhance your web forms with the following newly supported elements:
Fieldset()
: Create a <fieldset>
element to group related items within a form.Legend()
: Use the <legend>
element to caption your <fieldset>
groups.Datalist()
: Implement a <datalist>
element providing autocomplete suggestions to users.Meter()
: Display a <meter>
for representing scalar measurements like disk usage.Output()
: Incorporate an <output>
element to showcase the results of a calculation.Progress()
: Utilize a <progress>
bar to indicate the completion status of a task.Introducing functions for interactive elements that improve user engagement:
Dialog()
: Deploy modal <dialog>
elements for interactive pop-ups.Menu()
: Construct a <menu>
element for custom-built, interactive menu controls.For enhanced scripting capabilities:
NoScript()
: Define alternative content with <noscript>
for users without script support in their browsers.Published by chasefleming 12 months ago
Boolean attributes like checked and selected can now be assigned values of "true"
or "false"
. Setting them to true will correctly render these attributes without needing an explicit value. For instance:
// Using boolean attributes
checkbox := elem.Input(elem.Attrs{
attrs.Type: "checkbox",
attrs.Checked: "true", // This will render as <input type="checkbox" checked>
})
In the styles
subpackage you'll now find constants for the following:
font-variant
font-stretch
word-wrap
font-style
text-shadow
vertical-align
word-spacing
word-break
text-indent
background-position
background-attachment
background-blend-mode
backface-visibility
perspective
transform-origin
outline
outline-style
outline-color
outline-width
outline-offset
In the attrs
subpackage you'll now find constants for the following:
colspan
rowspan
headers
scope
is-map
novalidate
selected