- router.dnsProviders: named accounts at DNS providers, used by dynamic DNS and certificates (ddclient and lego provider lists in pkgs/router-webui/providers.json). Schema 2 migrates router.ddns. - router.acme: a Let's Encrypt certificate for the web UI via security.acme (DNS-01, resolver 1.1.1.1), served and reloaded by the web UI; Certificate page. - Web UI: password change (web UI and root), reboot button, HTTP to HTTPS redirect (router.webui.httpPort), grouped menu. - Latest kernel (linuxPackages_latest); 7.3 and 7.3.0 are the same kernel for the reboot hint. - SPECS.md: wanted later (REST API, GraphQL, MCP). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
84 lines
6.2 KiB
CSS
84 lines
6.2 KiB
CSS
:root { --fg: #1d2125; --muted: #5b636b; --bg: #fff; --line: #d6dade; --accent: #2457c5; --warn: #fff4d6; --bad: #b3261e; --ok: #e6f2e9; }
|
|
@media (prefers-color-scheme: dark) {
|
|
:root { --fg: #e4e7ea; --muted: #9aa2aa; --bg: #15181b; --line: #30363c; --accent: #7aa2ff; --warn: #3a3218; --bad: #ff8a80; --ok: #1d3324; }
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body { margin: 0; font: 15px/1.5 system-ui, sans-serif; color: var(--fg); background: var(--bg); }
|
|
header { display: flex; gap: 1.5rem; align-items: center; padding: .75rem 1rem; border-bottom: 1px solid var(--line); flex-wrap: wrap; }
|
|
nav { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; }
|
|
nav form { display: inline; }
|
|
nav details { position: relative; }
|
|
nav summary { cursor: pointer; color: var(--accent); list-style: none; }
|
|
nav summary::-webkit-details-marker { display: none; }
|
|
nav summary::after { content: " \25BE"; }
|
|
nav details > div { position: absolute; z-index: 10; top: 1.6rem; left: -.75rem; min-width: 11rem; display: flex; flex-direction: column; gap: .35rem;
|
|
padding: .6rem .75rem; background: var(--bg); border: 1px solid var(--line); border-radius: 6px; box-shadow: 0 4px 12px rgba(0, 0, 0, .15); }
|
|
a { color: var(--accent); }
|
|
main { max-width: 72rem; margin: 0 auto; padding: 1rem; }
|
|
h1 { font-size: 1.4rem; margin: .5rem 0 1rem; }
|
|
h2 { font-size: 1.1rem; margin: 0 0 .5rem; }
|
|
section { margin: 0 0 1.25rem; }
|
|
section.busy, section.pending { padding: .75rem 1rem; border-radius: 6px; background: var(--warn); }
|
|
.flash { padding: .5rem .75rem; background: var(--ok); border-radius: 6px; }
|
|
.error, .field-error { color: var(--bad); }
|
|
.field-error { font-size: .9em; }
|
|
.hint { color: var(--muted); font-size: .9em; margin: .25rem 0; }
|
|
dl { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; }
|
|
dt { color: var(--muted); }
|
|
dd { margin: 0; overflow-wrap: anywhere; }
|
|
fieldset { border: 1px solid var(--line); border-radius: 6px; margin: 0 0 1rem; padding: .75rem 1rem; overflow-x: auto; }
|
|
label { display: inline-flex; flex-direction: column; margin: 0 1rem .5rem 0; font-size: .9em; color: var(--muted); }
|
|
input, select, textarea, button { font: inherit; color: var(--fg); background: var(--bg); border: 1px solid var(--line); border-radius: 4px; padding: .3rem .5rem; }
|
|
textarea { width: 100%; font-family: ui-monospace, monospace; font-size: 13px; }
|
|
table { border-collapse: collapse; }
|
|
th { text-align: left; font-weight: 500; color: var(--muted); font-size: .85em; padding: 0 .3rem; }
|
|
td { padding: .15rem .3rem; }
|
|
button { background: var(--accent); color: var(--bg); border-color: var(--accent); cursor: pointer; padding: .4rem 1rem; }
|
|
button.secondary { background: transparent; color: var(--accent); }
|
|
button.link { background: none; border: none; color: var(--accent); padding: 0; text-decoration: underline; }
|
|
.actions { display: flex; gap: .75rem; flex-wrap: wrap; }
|
|
.narrow { max-width: 22rem; display: flex; flex-direction: column; gap: .75rem; }
|
|
.narrow label { margin: 0; }
|
|
pre.log { background: color-mix(in srgb, var(--line) 35%, transparent); padding: .75rem; border-radius: 6px; overflow-x: auto; font-size: 12.5px; max-height: 30rem; }
|
|
code { font-size: .9em; overflow-wrap: anywhere; }
|
|
.muted { color: var(--muted); }
|
|
.bad { color: var(--bad); }
|
|
.ok { color: var(--good); }
|
|
:root { --good: #1e7b34; --s0: #2457c5; --s1: #d9730d; --s2: #2f9e44; --s3: #9c36b5; --s4: #c92a2a; --s5: #0b7285; --s6: #862e9c; --s7: #5c940d; --s8: #a61e4d; --s9: #5f3dc4; }
|
|
@media (prefers-color-scheme: dark) { :root { --good: #69db7c; --s0: #7aa2ff; --s1: #ffa94d; --s2: #69db7c; --s3: #da77f2; --s4: #ff8787; --s5: #66d9e8; --s6: #e599f7; --s7: #c0eb75; --s8: #faa2c1; --s9: #b197fc; } }
|
|
.ranges { display: flex; gap: .25rem; margin: 0 0 1rem; }
|
|
.ranges a { padding: .2rem .7rem; border: 1px solid var(--line); border-radius: 4px; text-decoration: none; }
|
|
.ranges a.active { background: var(--accent); color: var(--bg); border-color: var(--accent); }
|
|
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: 1rem; margin: 0 0 1.25rem; }
|
|
.card { border: 1px solid var(--line); border-left-width: 4px; border-radius: 6px; padding: .75rem 1rem; margin: 0; }
|
|
.card.up { border-left-color: var(--good); }
|
|
.card.degraded { border-left-color: #e8a600; }
|
|
.card.down { border-left-color: var(--bad); }
|
|
.card .state { font-weight: 600; margin: 0 0 .5rem; }
|
|
.card dl { margin: 0; font-size: .92em; }
|
|
.events { list-style: none; margin: 0; padding: 0; font-size: .9em; }
|
|
.events li span:first-child { display: inline-block; width: 2.5rem; font-weight: 600; }
|
|
.scroll { overflow-x: auto; }
|
|
.leases td, .leases th { padding: .2rem .75rem .2rem 0; white-space: nowrap; }
|
|
.charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 30rem), 1fr)); gap: 1rem; margin: 0 0 1.5rem; }
|
|
.chart { margin: 0; border: 1px solid var(--line); border-radius: 6px; padding: .5rem .75rem; min-width: 0; }
|
|
.chart figcaption { display: flex; flex-wrap: wrap; gap: .25rem 1rem; justify-content: space-between; font-size: .9em; }
|
|
.legend { display: flex; flex-wrap: wrap; gap: .1rem .9rem; }
|
|
.key::before { content: ""; display: inline-block; width: .7rem; height: .2rem; margin: 0 .3rem .2rem 0; background: currentColor; vertical-align: middle; }
|
|
.key b { font-weight: 500; color: var(--fg); }
|
|
.chart svg { width: 100%; height: auto; display: block; overflow: visible; }
|
|
.chart .grid { stroke: var(--line); stroke-width: 1; }
|
|
.chart .lab { fill: var(--muted); font-size: 11px; }
|
|
.chart .line { fill: none; stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
|
|
.chart .hover { fill: transparent; }
|
|
.chart .hover:hover { fill: color-mix(in srgb, var(--fg) 8%, transparent); }
|
|
.s0 { color: var(--s0); stroke: var(--s0); } .s1 { color: var(--s1); stroke: var(--s1); } .s2 { color: var(--s2); stroke: var(--s2); }
|
|
.s3 { color: var(--s3); stroke: var(--s3); } .s4 { color: var(--s4); stroke: var(--s4); }
|
|
.s5 { color: var(--s5); stroke: var(--s5); } .s6 { color: var(--s6); stroke: var(--s6); } .s7 { color: var(--s7); stroke: var(--s7); } .s8 { color: var(--s8); stroke: var(--s8); } .s9 { color: var(--s9); stroke: var(--s9); }
|
|
|
|
/* WireGuard */
|
|
.qr svg { width: 14rem; height: 14rem; background: #fff; }
|
|
pre { overflow-x: auto; white-space: pre-wrap; word-break: break-all; }
|
|
form.inline { margin-top: 0.5rem; }
|
|
details { margin: 0.5rem 0; }
|