Skip to content

Commit 2491736

Browse files
committed
Polish web balance table styling
- Color positive amounts green and negative amounts red. - Stripe rows per logical entry (multi-commodity rows stay unified). - Top-align the account column so multi-row entries read cleanly. - Split amounts into integer/fractional cells aligned on the dot. - Shrink the table to its content so the commodity sits next to the amount and the account column takes only the width it needs. - Left-align the "Amount" header.
1 parent af27463 commit 2491736

2 files changed

Lines changed: 77 additions & 15 deletions

File tree

src/app.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,44 +52,77 @@ fn BalanceTable(entries: Vec<BalanceEntry>) -> impl IntoView {
5252
<thead>
5353
<tr>
5454
<th class="col-account">"Account"</th>
55-
<th class="col-amount">"Amount"</th>
55+
<th class="col-amount" colspan="2">"Amount"</th>
5656
<th class="col-commodity">"Commodity"</th>
5757
</tr>
5858
</thead>
5959
<tbody>
6060
{entries
6161
.into_iter()
62-
.map(|entry| view! { <BalanceRow entry /> })
62+
.enumerate()
63+
.map(|(i, entry)| {
64+
let stripe = if i % 2 == 0 { "even" } else { "odd" };
65+
view! { <BalanceRow entry stripe /> }
66+
})
6367
.collect_view()}
6468
</tbody>
6569
</table>
6670
}
6771
}
6872

73+
fn sign_class(value: &str) -> &'static str {
74+
if value.trim_start().starts_with('-') {
75+
"negative"
76+
} else if value.trim().is_empty() {
77+
""
78+
} else {
79+
"positive"
80+
}
81+
}
82+
83+
fn split_amount(value: &str) -> (String, String) {
84+
match value.find('.') {
85+
Some(i) => (value[..i].to_string(), value[i..].to_string()),
86+
None => (value.to_string(), String::new()),
87+
}
88+
}
89+
6990
#[component]
70-
fn BalanceRow(entry: BalanceEntry) -> impl IntoView {
71-
let class = if entry.is_group { "group" } else { "leaf" };
91+
fn BalanceRow(entry: BalanceEntry, stripe: &'static str) -> impl IntoView {
92+
let kind = if entry.is_group { "group" } else { "leaf" };
93+
let row_class = format!("{kind} {stripe}");
7294
let mut amounts = entry.amounts;
7395
if amounts.is_empty() {
7496
amounts.push(("".to_string(), "".to_string()));
7597
}
7698
let first = amounts.remove(0);
7799
let rowspan = 1 + amounts.len();
100+
let sign = sign_class(&first.1);
101+
let int_class = format!("col-amount-int {sign}");
102+
let frac_class = format!("col-amount-frac {sign}");
103+
let (int_part, frac_part) = split_amount(&first.1);
78104

79105
view! {
80-
<tr class=class>
106+
<tr class=row_class.clone()>
81107
<td class="col-account" rowspan=rowspan>
82108
{entry.account}
83109
</td>
84-
<td class="col-amount">{first.1}</td>
110+
<td class=int_class>{int_part}</td>
111+
<td class=frac_class>{frac_part}</td>
85112
<td class="col-commodity">{first.0}</td>
86113
</tr>
87114
{amounts
88115
.into_iter()
89116
.map(|(commodity, value)| {
117+
let sign = sign_class(&value);
118+
let int_class = format!("col-amount-int {sign}");
119+
let frac_class = format!("col-amount-frac {sign}");
120+
let (int_part, frac_part) = split_amount(&value);
121+
let row_class = row_class.clone();
90122
view! {
91-
<tr class=class>
92-
<td class="col-amount">{value}</td>
123+
<tr class=row_class>
124+
<td class=int_class>{int_part}</td>
125+
<td class=frac_class>{frac_part}</td>
93126
<td class="col-commodity">{commodity}</td>
94127
</tr>
95128
}

style/main.css

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
--color-error: hsl(0, 70%, 60%);
1111
--color-group: hsl(190, 50%, 65%);
1212
--color-leaf-account: hsl(210, 50%, 70%);
13+
--color-positive: hsl(135, 50%, 60%);
14+
--color-negative: hsl(0, 70%, 65%);
15+
--bg-stripe: hsl(220, 15%, 11%);
1316
}
1417

1518
@media (prefers-color-scheme: light) {
@@ -23,6 +26,9 @@
2326
--color-error: hsl(0, 70%, 42%);
2427
--color-group: hsl(190, 70%, 30%);
2528
--color-leaf-account: hsl(210, 70%, 38%);
29+
--color-positive: hsl(135, 60%, 30%);
30+
--color-negative: hsl(0, 70%, 42%);
31+
--bg-stripe: hsl(220, 15%, 94%);
2632
}
2733
}
2834

@@ -82,7 +88,6 @@ main {
8288
}
8389

8490
table {
85-
width: 100%;
8691
border-collapse: collapse;
8792
font-family:
8893
"SF Mono", "Fira Code", "Fira Mono",
@@ -101,14 +106,18 @@ thead th {
101106
letter-spacing: 0.05em;
102107
}
103108

104-
th.col-amount {
105-
text-align: right;
106-
}
107-
108109
tbody td {
109110
padding: 0.25rem 1rem;
110111
}
111112

113+
td.col-account {
114+
vertical-align: top;
115+
}
116+
117+
tbody tr.odd td {
118+
background-color: var(--bg-stripe);
119+
}
120+
112121
tr.group td {
113122
padding-top: 1.25rem;
114123
font-weight: 600;
@@ -124,11 +133,31 @@ tr.leaf td.col-account {
124133
padding-left: 2rem;
125134
}
126135

127-
td.col-amount {
128-
text-align: right;
136+
td.col-amount-int,
137+
td.col-amount-frac {
129138
font-variant-numeric: tabular-nums;
130139
}
131140

141+
td.col-amount-int {
142+
text-align: right;
143+
padding-right: 0;
144+
}
145+
146+
td.col-amount-frac {
147+
text-align: left;
148+
padding-left: 0;
149+
}
150+
151+
td.col-amount-int.positive,
152+
td.col-amount-frac.positive {
153+
color: var(--color-positive);
154+
}
155+
156+
td.col-amount-int.negative,
157+
td.col-amount-frac.negative {
158+
color: var(--color-negative);
159+
}
160+
132161
td.col-commodity {
133162
color: var(--color-muted);
134163
padding-left: 0.5rem;

0 commit comments

Comments
 (0)