-
-
Notifications
You must be signed in to change notification settings - Fork 288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to parameterize styles or css components #173
Comments
Thanks for bringing this up. Paramaters for CSS expressions is something we want to add. We started talking about this over at #88 One approach is to use utility classes to do it, e.g. https://tailwindcss.com/docs/grid-template-columns package main
import "fmt"
func getColsClass(cols int) string {
return fmt.Sprintf("grid-cols-%d", cols)
}
templ ColsTest(cols int) {
<div class={ "grid", getColsClass(cols), fmt.Sprintf("gap-%d", cols) }>
<div>01</div>
<!-- ... -->
<div>09</div>
</div>
} With the corresponding package main
import (
"context"
"fmt"
"io"
"os"
"github.com/a-h/htmlformat"
)
func main() {
r, w := io.Pipe()
go func() {
ColsTest(4).Render(context.Background(), w)
w.Close()
}()
err := htmlformat.Fragment(os.Stdout, r)
if err != nil {
fmt.Println("failed to format")
}
} You get the HTML output of: <div class="grid grid-cols-4 gap-4">
<div>
01
</div>
<!-- ... -->
<div>
09
</div>
</div> Another way is to use a custom class component. Since templ generates Go code, there's usually a way to bypass templ completely and do whatever you want. For example, , you can use a custom CSS class component ( package main
import "fmt"
templ ColsTest(cols int) {
<div class={ "grid", ColumnsClassComponent(cols), fmt.Sprintf("gap-%d", cols) }>
<div>Grid 1</div>
</div>
<div class={ "grid", ColumnsClassComponent(cols), fmt.Sprintf("gap-%d", cols) }>
<div>Grid 2</div>
</div>
<div class={ "grid", ColumnsClassComponent(3), fmt.Sprintf("gap-%d", cols) }>
<div>Grid 3</div>
</div>
} So here, you can create a package main
import (
"context"
"fmt"
"io"
"os"
"github.com/a-h/htmlformat"
"github.com/a-h/templ"
)
func ColumnsClassComponent(cols int) templ.ComponentCSSClass {
id := fmt.Sprintf("grid-cols-%d", cols)
return templ.ComponentCSSClass{
ID: id,
Class: templ.SafeCSS(fmt.Sprintf(`.%s {
display: grid;
grid-template-columns: repeat(%d, 1fr);
grid-gap: 1rem;
}`, id, cols)),
}
}
func main() {
r, w := io.Pipe()
go func() {
ColsTest(4).Render(context.Background(), w)
w.Close()
}()
err := htmlformat.Fragment(os.Stdout, r)
if err != nil {
fmt.Println("failed to format")
}
} But, as you can see from the output. templ takes care of conditional rendering of the required CSS classes - there's no waste in rendering classes that aren't used, or multiple copies of the same <style type="text/css">
.grid-cols-4 {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 1rem;
}
</style>
<div class="grid grid-cols-4 gap-4">
<div>
Grid 1
</div>
</div>
<div class="grid grid-cols-4 gap-4">
<div>
Grid 2
</div>
</div>
<style type="text/css">
.grid-cols-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 1rem;
}
</style>
<div class="grid grid-cols-3 gap-4">
<div>
Grid 3
</div>
</div> This concept isn't in the docs at the moment. I think that it would be better to have CSS params though! |
Thanks a lot for the detailed and thought through response! I went with a little cheeky
And it works great. But I consider this using undocumented/unformalized behaviour and wouldn't be mad or surprised if it's stops working in a future release. It would be great to have this behaviour formalized in a future version at some point. From my perspective it would be nice to allow parameters to css components. But I'm not sure how things like fmt.Sprintf (and sanitization?) would work in a smooth way. |
One thing I was thinking about: CSS Components get formalized in a way that can generate classes on the fly with dynamic data, it would probably be nice to have the context.Context injected there too. Just like template components have. The point being that different classes can be generated depending on what user is visiting. E.g. for dark vs light mode |
I have found a way to get a dynamic style attribute... In the templ file: <image style="REPLACE_THIS_STYLE_POSITION" height="32px" src="..."></image> Then in the HandlerFunc: sb := new(strings.Builder)
index := public.Index(...)
index.Render(r.Context(), sb)
// This is the worst, but templating the style attribute is
// not allowed for security reasons.
out := strings.Replace(
sb.String(),
`style="REPLACE_THIS_STYLE_POSITION"`,
fmt.Sprintf(
`style="position: absolute; top: %dpx; left: %dpx; transform: translate(-50%%, -50%%)"`,
y,
x,
),
1,
)
w.Write([]byte(out)) |
This has ran it's course I believe, css parameters will be tracked in #88 |
Hello!
Loving templ, having a blast with it.
I've been reading the docs, but I'm stumped on how to get a variable into a style or css.
I'm trying to render a grid, and would like to do something like:
But it seems that css components don't take parameters: views/items.templ: views/items.templ parsing error: css expression: found unexpected parameters: line 30, col 25
I also tried to do:
But it just rendered as text. And I was unable to use an expression in the "style" attribute, as noted on https://templ.guide/security/
So yeah, I guess my question is how should I work with CSS Grids in templ? I need to input some row and col data. Like for instance shown at https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout
And I don't know at design-time how the grid will look, since it's based on user input
The text was updated successfully, but these errors were encountered: