fix(visual): center PIN dots with symmetric field padding
go-text already omits outer letter-spacing, so the asymmetric padding shifted the dots right. The relief border width now comes from fieldEdge.
- commit
- 1fd224555ffeefcf30c48423adfce7dcf205e9db
- author
- brice · · 2026-10-01 13:32 UTC
- committer
- brice · · 2026-10-01 16:10 UTC
- parent
- 9e2fc29
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +5 -7
M internal/adapters/visual/view.go +5 -7
| @@ -25,7 +25,6 @@ const ( | |||
| 25 | 25 | fieldEdge = 2 // relief width (bottom and right) | |
| 26 | 26 | passwordW = 280 | |
| 27 | 27 | pinW = 160 | |
| 28 | - | pinSpacing = 6 // letter-spacing between PIN dots | |
| 29 | 28 | ||
| 30 | 29 | // boxContentW is the width available to the field inside the box. | |
| 31 | 30 | boxContentW = frameW - 2*boxBorder - 2*boxPadding | |
| @@ -42,8 +41,6 @@ const ( | |||
| 42 | 41 | cssClock = "color:#ffffff;text-align:right;height:24px;margin-bottom:8px" | |
| 43 | 42 | // cssBalance mirrors cssClock below the box. | |
| 44 | 43 | cssBalance = "height:24px;margin-top:8px" | |
| 45 | - | // cssRelief is the inverted relief: bottom and right edges at 50% white. | |
| 46 | - | cssRelief = "border-bottom:2px solid rgba(255,255,255,0.5);border-right:2px solid rgba(255,255,255,0.5)" | |
| 47 | 44 | // cssHint and cssHintBalance have the same outer height. | |
| 48 | 45 | cssHint = "color:#ffffff;margin-top:12px;height:22px;text-align:center" | |
| 49 | 46 | cssHintBalance = "margin-bottom:12px;height:22px" | |
| @@ -52,16 +49,17 @@ const ( | |||
| 52 | 49 | ) | |
| 53 | 50 | ||
| 54 | 51 | var ( | |
| 52 | + | // cssRelief is the inverted relief: bottom and right edges at 50% white. | |
| 53 | + | cssRelief = fmt.Sprintf("border-bottom:%[1]dpx solid rgba(255,255,255,0.5);"+ | |
| 54 | + | "border-right:%[1]dpx solid rgba(255,255,255,0.5)", fieldEdge) | |
| 55 | 55 | cssFrame = fmt.Sprintf("display:flex;flex-direction:column;align-items:stretch;width:%dpx", frameW) | |
| 56 | 56 | cssBox = fmt.Sprintf("display:flex;flex-direction:column;align-items:stretch;"+ | |
| 57 | 57 | "border:%dpx solid #bfbfbf;padding:%dpx;background-color:#000000", boxBorder, boxPadding) | |
| 58 | 58 | cssMask = fmt.Sprintf("color:#ffffff;height:32px;line-height:32px;width:%dpx;"+ | |
| 59 | 59 | "padding:4px %dpx;margin-left:%dpx;%s", passwordW, fieldPadX, fieldMargin(passwordW), cssRelief) | |
| 60 | - | // The PIN field shifts its padding by half the trailing letter-spacing | |
| 61 | - | // so the dots stay optically centered. | |
| 62 | 60 | cssMaskPIN = fmt.Sprintf("color:#ffffff;height:32px;line-height:32px;width:%dpx;"+ | |
| 63 | - | "padding:4px %dpx 4px %dpx;margin-left:%dpx;font-size:24px;letter-spacing:%dpx;text-align:center;%s", | |
| 64 | - | pinW, fieldPadX-pinSpacing/2, fieldPadX+pinSpacing/2, fieldMargin(pinW), pinSpacing, cssRelief) | |
| 61 | + | "padding:4px %dpx;margin-left:%dpx;font-size:24px;letter-spacing:6px;text-align:center;%s", | |
| 62 | + | pinW, fieldPadX, fieldMargin(pinW), cssRelief) | |
| 65 | 63 | ) | |
| 66 | 64 | ||
| 67 | 65 | // fieldMargin centers a field of content width w inside the box content. | |
| @@ -25,7 +25,6 @@ const ( | |||||
| 25 | fieldEdge = 2 // relief width (bottom and right) | 25 | fieldEdge = 2 // relief width (bottom and right) | ||
| 26 | passwordW = 280 | 26 | passwordW = 280 | ||
| 27 | pinW = 160 | 27 | pinW = 160 | ||
| 28 | - | pinSpacing = 6 // letter-spacing between PIN dots | |||
| 29 | 28 | ||||
| 30 | // boxContentW is the width available to the field inside the box. | 29 | // boxContentW is the width available to the field inside the box. | ||
| 31 | boxContentW = frameW - 2*boxBorder - 2*boxPadding | 30 | boxContentW = frameW - 2*boxBorder - 2*boxPadding | ||
| @@ -42,8 +41,6 @@ const ( | |||||
| 42 | cssClock = "color:#ffffff;text-align:right;height:24px;margin-bottom:8px" | 41 | cssClock = "color:#ffffff;text-align:right;height:24px;margin-bottom:8px" | ||
| 43 | // cssBalance mirrors cssClock below the box. | 42 | // cssBalance mirrors cssClock below the box. | ||
| 44 | cssBalance = "height:24px;margin-top:8px" | 43 | cssBalance = "height:24px;margin-top:8px" | ||
| 45 | - | // cssRelief is the inverted relief: bottom and right edges at 50% white. | |||
| 46 | - | cssRelief = "border-bottom:2px solid rgba(255,255,255,0.5);border-right:2px solid rgba(255,255,255,0.5)" | |||
| 47 | // cssHint and cssHintBalance have the same outer height. | 44 | // cssHint and cssHintBalance have the same outer height. | ||
| 48 | cssHint = "color:#ffffff;margin-top:12px;height:22px;text-align:center" | 45 | cssHint = "color:#ffffff;margin-top:12px;height:22px;text-align:center" | ||
| 49 | cssHintBalance = "margin-bottom:12px;height:22px" | 46 | cssHintBalance = "margin-bottom:12px;height:22px" | ||
| @@ -52,16 +49,17 @@ const ( | |||||
| 52 | ) | 49 | ) | ||
| 53 | 50 | ||||
| 54 | var ( | 51 | var ( | ||
| 52 | + | // cssRelief is the inverted relief: bottom and right edges at 50% white. | |||
| 53 | + | cssRelief = fmt.Sprintf("border-bottom:%[1]dpx solid rgba(255,255,255,0.5);"+ | |||
| 54 | + | "border-right:%[1]dpx solid rgba(255,255,255,0.5)", fieldEdge) | |||
| 55 | cssFrame = fmt.Sprintf("display:flex;flex-direction:column;align-items:stretch;width:%dpx", frameW) | 55 | cssFrame = fmt.Sprintf("display:flex;flex-direction:column;align-items:stretch;width:%dpx", frameW) | ||
| 56 | cssBox = fmt.Sprintf("display:flex;flex-direction:column;align-items:stretch;"+ | 56 | cssBox = fmt.Sprintf("display:flex;flex-direction:column;align-items:stretch;"+ | ||
| 57 | "border:%dpx solid #bfbfbf;padding:%dpx;background-color:#000000", boxBorder, boxPadding) | 57 | "border:%dpx solid #bfbfbf;padding:%dpx;background-color:#000000", boxBorder, boxPadding) | ||
| 58 | cssMask = fmt.Sprintf("color:#ffffff;height:32px;line-height:32px;width:%dpx;"+ | 58 | cssMask = fmt.Sprintf("color:#ffffff;height:32px;line-height:32px;width:%dpx;"+ | ||
| 59 | "padding:4px %dpx;margin-left:%dpx;%s", passwordW, fieldPadX, fieldMargin(passwordW), cssRelief) | 59 | "padding:4px %dpx;margin-left:%dpx;%s", passwordW, fieldPadX, fieldMargin(passwordW), cssRelief) | ||
| 60 | - | // The PIN field shifts its padding by half the trailing letter-spacing | |||
| 61 | - | // so the dots stay optically centered. | |||
| 62 | cssMaskPIN = fmt.Sprintf("color:#ffffff;height:32px;line-height:32px;width:%dpx;"+ | 60 | cssMaskPIN = fmt.Sprintf("color:#ffffff;height:32px;line-height:32px;width:%dpx;"+ | ||
| 63 | - | "padding:4px %dpx 4px %dpx;margin-left:%dpx;font-size:24px;letter-spacing:%dpx;text-align:center;%s", | 61 | + | "padding:4px %dpx;margin-left:%dpx;font-size:24px;letter-spacing:6px;text-align:center;%s", |
| 64 | - | pinW, fieldPadX-pinSpacing/2, fieldPadX+pinSpacing/2, fieldMargin(pinW), pinSpacing, cssRelief) | 62 | + | pinW, fieldPadX, fieldMargin(pinW), cssRelief) |
| 65 | ) | 63 | ) | ||
| 66 | 64 | ||||
| 67 | // fieldMargin centers a field of content width w inside the box content. | 65 | // fieldMargin centers a field of content width w inside the box content. | ||