Add mobile nav toggle, responsive admin tables, and menu reordering
- Public site: replace the always-hidden mobile nav with a hamburger toggle (index.php, menu.php, main.js, styles.css) - Admin: render the Menu/Announcements/Users tables as labeled cards below ~760px instead of squeezing descriptions into unreadable single-word-per-line columns - Admin menu: replace the manual "Order" number (which allowed duplicates and never reflected in real time) with ↑/↓ buttons that reorder items and categories immediately and renumber sort_order sequentially on every move, self-healing any stale duplicates Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7c96b952e9
commit
40f4c102b4
10 files changed
+553
-94
No files matched your search
+149
@@ -343,6 +343,10 @@
|
||||
color: var(--gold-bright);
|
||||
}
|
||||
|
||||
nav.links a.active {
|
||||
color: var(--gold-bright);
|
||||
}
|
||||
|
||||
.nav-cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -355,9 +359,67 @@
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
/* mobile nav toggle: hidden on desktop, shown only under the nav.links
|
||||
breakpoint below. Icon swap is driven by the button's own
|
||||
aria-expanded state rather than a second JS-toggled class. */
|
||||
.nav-toggle {
|
||||
display: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0.5rem;
|
||||
margin: -0.5rem;
|
||||
color: var(--ink);
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-toggle svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.nav-toggle .icon-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-toggle[aria-expanded="true"] .icon-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-toggle[aria-expanded="true"] .icon-close {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 47.5em) {
|
||||
.nav-toggle {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.nav-cta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav.links {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
background: var(--ground-2);
|
||||
border-bottom: 1px solid var(--rule);
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
nav.links.is-open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
nav.links a {
|
||||
padding: 0.9rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,6 +532,12 @@
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* :where() keeps this native-button reset at zero specificity, so
|
||||
modifier classes like .btn-primary's background can actually win
|
||||
instead of losing to button.btn's higher (0,1,1) specificity */
|
||||
@@ -1124,6 +1192,22 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 47.5em) {
|
||||
.admin-nav-inner {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* two classes each, so these reliably outrank .admin-nav-links's and
|
||||
.admin-user's own base-rule gap regardless of source order */
|
||||
.admin-nav-inner .admin-nav-links,
|
||||
.admin-nav-inner .admin-user {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-layout {
|
||||
padding-block: 2.5rem 4rem;
|
||||
}
|
||||
@@ -1155,8 +1239,10 @@
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
@@ -1279,12 +1365,74 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Below ~760px a plain <table> forces long text (item descriptions,
|
||||
headlines) into a column so narrow it wraps one word per line. Recast
|
||||
each row as a labeled card instead — the label comes from the td's
|
||||
data-label attribute, kept in sync with the <th> text in each admin
|
||||
page (and its matching *.js row template, where rows are rebuilt
|
||||
client-side). */
|
||||
@media (max-width: 47.5em) {
|
||||
.admin-table-wrap {
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.admin-table thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admin-table,
|
||||
.admin-table tbody,
|
||||
.admin-table tr,
|
||||
.admin-table td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.admin-table tr {
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.9rem 1rem;
|
||||
margin-bottom: 0.85rem;
|
||||
}
|
||||
|
||||
.admin-table tr:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.admin-table td {
|
||||
padding: 0.4rem 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.admin-table td[data-label]::before {
|
||||
content: attr(data-label);
|
||||
display: block;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: var(--gold);
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.admin-table-actions {
|
||||
flex-wrap: wrap;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.4rem 0.8rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Single-glyph reorder buttons (↑ / ↓) — narrower than .btn-sm's text
|
||||
padding since there's no label to give it room to breathe. */
|
||||
.btn-icon {
|
||||
padding-inline: 0.55rem;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: var(--danger-bg);
|
||||
border: 1px solid rgba(220, 80, 60, 0.5);
|
||||
@@ -1539,6 +1687,7 @@
|
||||
|
||||
.admin-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
Reference in new issue
Block a user