Fix text alignment

This commit is contained in:
Jonathon Leight committed 2026-08-04 20:06:34 -04:00
1 parent 7e5056b4d2
commit 2e71bdab82
3 files changed
+60 -1

No files matched your search

+6
View File
@@ -121,6 +121,12 @@ markup for it. The one that has already bitten us:
this; it parses the templates structurally, so extend it there if you find
another flex-container component with the same trap.
- **A `<button>` centers its text.** Invisible on a normal button, wrong the
moment you use one as a full-width row (`d-flex w-100`, e.g. a `list-group-item`
that expands a collapse): the label sits centered while every neighboring row is
left-aligned. **Add `text-start`.** `TestFullWidthFlexButtonsAreTextStart`, in
the same file, enforces it.
### 5. CI is the gate, not the first line
Woodpecker (`.woodpecker/`): `test` + `lint` + `vuln` (govulncheck) must pass
before `build`; `deploy` follows `build` on `main`. A reachable CVE blocks the
@@ -39,7 +39,10 @@
{{range $i, $l := .Links}}
{{if $l.IsMeshCore}}
<div class="list-group-item">
<button type="button" class="d-flex align-items-center gap-2 text-reset border-0 bg-transparent p-0 w-100" data-bs-toggle="collapse" data-bs-target="#mesh-{{$i}}" aria-expanded="false" aria-controls="mesh-{{$i}}">
{{/* text-start because this row is a <button>, and a button centers its
text by default — without it this one label sits centered while every
other row in the list is left-aligned. */}}
<button type="button" class="d-flex align-items-center gap-2 text-reset text-start border-0 bg-transparent p-0 w-100" data-bs-toggle="collapse" data-bs-target="#mesh-{{$i}}" aria-expanded="false" aria-controls="mesh-{{$i}}">
<span class="text-secondary d-flex" title="{{$l.PlatformName}}">{{template "link-icon" $l.Platform}}</span>
<span class="flex-fill text-truncate">{{$l.Display}}</span>
<span class="text-secondary d-flex">{{template "icon-qrcode" ""}}</span>
+50
View File
@@ -76,6 +76,56 @@ func TestAlertBodiesAreSingleChild(t *testing.T) {
}
}
// TestFullWidthFlexButtonsAreTextStart is the same class of trap as
// TestAlertBodiesAreSingleChild: a component whose default styling quietly
// contradicts the layout it's being used for.
//
// A <button> centers its text. That's invisible on an ordinary button, whose text
// is the whole content — but a full-width button used as a row (d-flex + w-100,
// e.g. the MeshCore key row in the public profile's Links list) inherits that
// centering into every child, so its label floats in the middle of the row while
// every neighboring row is left-aligned. Nothing errors; it just looks wrong next
// to the rows around it.
//
// The fix is always the same: add text-start.
func TestFullWidthFlexButtonsAreTextStart(t *testing.T) {
t.Parallel()
root := moduleRoot(t)
// Buttons only: an <a> or <div> laid out the same way already inherits the
// page's left alignment. w-100 scopes this to buttons acting as rows.
buttonRe := regexp.MustCompile(`(?is)<button[^>]*class="([^"]*)"[^>]*>`)
err := filepath.WalkDir(filepath.Join(root, "internal"), func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || !strings.HasSuffix(path, ".html") {
return nil
}
b, err := os.ReadFile(path)
if err != nil {
return err
}
rel, _ := filepath.Rel(root, path)
for _, m := range buttonRe.FindAllSubmatch(b, -1) {
classes := map[string]bool{}
for _, c := range strings.Fields(string(m[1])) {
classes[c] = true
}
if classes["d-flex"] && classes["w-100"] && !classes["text-start"] {
t.Errorf("%s: full-width flex <button> is missing text-start — a button "+
"centers its text, so this row's label sits centered while the rows "+
"around it are left-aligned. Found class=%q", rel, m[1])
}
}
return nil
})
if err != nil {
t.Fatalf("walk templates: %v", err)
}
}
// findAlerts returns every element carrying the bare "alert" class. Modifier
// classes (alert-title, alert-icon, alert-heading) are not flex containers and
// are deliberately excluded.