UI style refresh: Final cleanup

This commit is contained in:
Jim Lancaster
2026-08-03 16:27:02 -05:00
parent f80834d589
commit 323f12d911
11 changed files with 96 additions and 38 deletions
@@ -4,10 +4,16 @@ from transcription.ui.components.app_shell import NAV_ITEMS
from transcription.ui.components.app_shell import render_app_shell
from transcription.ui.components.app_shell import render_navigation_header
from transcription.ui.components.document_panzoom import render_document_panzoom
from transcription.ui.components.primitives import destructive_button
from transcription.ui.components.primitives import render_empty_state
from transcription.ui.components.primitives import section_header_row
__all__ = [
"NAV_ITEMS",
"destructive_button",
"render_app_shell",
"render_document_panzoom",
"render_empty_state",
"render_navigation_header",
"section_header_row",
]
@@ -17,3 +17,22 @@ def render_empty_state(message: str, *, italic: bool = False, extra_classes: str
if italic:
classes = f"{classes} italic"
ui.label(message).classes(f"{classes} {extra_classes}".strip())
def destructive_button(
label: str,
*,
on_click,
icon: str,
variant: str = "outlined",
extra_classes: str = "",
):
"""Render a standardized destructive action button."""
button = ui.button(label, on_click=on_click, icon=icon)
if variant == "solid":
button.props("unelevated color=negative")
else:
button.props("outlined color=negative")
if extra_classes:
button.classes(extra_classes)
return button
@@ -10,6 +10,7 @@ from uuid import UUID
from nicegui import ui
from transcription.ui.components.cards import archival_card
from transcription.ui.components.primitives import render_empty_state
from transcription.ui.components.table.common import build_table
@@ -41,7 +42,7 @@ def render_documents_table(rows: Sequence[DocumentTableRow]) -> None:
"""Render documents table and open detail page when clicking a row."""
if not rows:
with archival_card(extra_classes="p-8 text-center"):
ui.label("No documents in repository yet.").classes("text-xs ui-text-muted")
render_empty_state("No documents in repository yet.")
return
build_table(
@@ -12,6 +12,7 @@ from uuid import UUID
from nicegui import ui
from transcription.ui.components.cards import archival_card
from transcription.ui.components.primitives import render_empty_state
from .common import build_table
@@ -57,7 +58,7 @@ def render_jobs_table(rows: Sequence[JobTableRow]) -> None:
"""Render jobs table and open a detail page when clicking a row."""
if not rows:
with archival_card(extra_classes="p-8 text-center"):
ui.label("No active or historical processing jobs found.").classes("text-xs ui-text-muted")
render_empty_state("No active or historical processing jobs found.")
return
build_table(
@@ -10,6 +10,7 @@ from uuid import UUID
from nicegui import ui
from transcription.ui.components.cards import archival_card
from transcription.ui.components.primitives import render_empty_state
from transcription.ui.components.table.common import build_table
@@ -41,7 +42,7 @@ def render_people_table(rows: Sequence[PersonTableRow]) -> None:
"""Render people table and open detail page when clicking a row."""
if not rows:
with archival_card(extra_classes="p-8 text-center"):
ui.label("No person records found in repository.").classes("text-xs ui-text-muted")
render_empty_state("No person records found in repository.")
return
build_table(
@@ -10,6 +10,7 @@ from uuid import UUID
from nicegui import ui
from transcription.ui.components.cards import archival_card
from transcription.ui.components.primitives import render_empty_state
from transcription.ui.components.table.common import build_table
@@ -41,7 +42,7 @@ def render_sources_table(rows: Sequence[SourceTableRow]) -> None:
"""Render sources table and open detail page when clicking a row."""
if not rows:
with archival_card(extra_classes="p-8 text-center"):
ui.label("No source file records found.").classes("text-xs ui-text-muted")
render_empty_state("No source file records found.")
return
build_table(
+7 -4
View File
@@ -21,6 +21,7 @@ from transcription.ui.components.cards import archival_card
from transcription.ui.components.data_display import archival_badge
from transcription.ui.components.data_display import metadata_row
from transcription.ui.components.error_presenter import show_error
from transcription.ui.components.primitives import destructive_button
from transcription.ui.components.primitives import render_empty_state
from transcription.ui.components.primitives import section_header_row
from transcription.ui.components.table.documents import DocumentTableRow
@@ -222,11 +223,12 @@ def register_page() -> None:
on_click=lambda: ui.navigate.to(f"/documents/{document.id}/edit"),
icon="edit",
).classes("ui-btn-primary text-xs")
ui.button(
destructive_button(
"Delete",
on_click=lambda: ui.navigate.to(f"/documents/{document.id}/delete"),
icon="delete",
).props("outlined color=negative text-xs")
extra_classes="text-xs",
)
# High-Density Bento Grid Layout
with ui.grid().classes("w-full grid-cols-12 gap-4"):
@@ -587,11 +589,12 @@ def register_page() -> None:
ui.navigate.to("/documents")
with ui.row().classes("w-full items-center gap-2 mt-2"):
ui.button(
destructive_button(
"Delete document permanently",
on_click=submit_delete,
icon="delete_forever",
).props("unelevated color=negative")
variant="solid",
)
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/documents/{document.id}"), icon="arrow_back").props(
"flat"
)
+9 -4
View File
@@ -19,6 +19,7 @@ from transcription.ui.components.cards import archival_card
from transcription.ui.components.data_display import archival_badge
from transcription.ui.components.data_display import metadata_row
from transcription.ui.components.error_presenter import show_error
from transcription.ui.components.primitives import destructive_button
from transcription.ui.components.primitives import render_empty_state
from transcription.ui.components.primitives import section_header_row
from transcription.ui.components.table.jobs import render_jobs_table
@@ -224,11 +225,12 @@ def register_page() -> None: # noqa: PLR0915
page_header(f"Job Record: {job.id}")
with ui.row().classes("items-center gap-2"):
archival_badge(job.status.value.upper())
ui.button(
destructive_button(
"Delete Job",
on_click=lambda: ui.navigate.to(f"/jobs/{job.id}/delete"),
icon="delete",
).props("outlined color=negative text-xs")
extra_classes="text-xs",
)
with ui.grid().classes("w-full grid-cols-1 md:grid-cols-2 gap-4"):
with archival_card(title="Execution Logistics"):
@@ -314,7 +316,10 @@ def register_page() -> None: # noqa: PLR0915
ui.navigate.to("/jobs")
with ui.row().classes("w-full items-center gap-2 mt-2"):
ui.button("Delete job permanently", on_click=submit_delete, icon="delete_forever").props(
"unelevated color=negative"
destructive_button(
"Delete job permanently",
on_click=submit_delete,
icon="delete_forever",
variant="solid",
)
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/jobs/{job.id}"), icon="arrow_back").props("flat")
+7 -4
View File
@@ -22,6 +22,7 @@ from transcription.ui.components.app_shell import render_navigation_header
from transcription.ui.components.cards import archival_card
from transcription.ui.components.data_display import metadata_row
from transcription.ui.components.error_presenter import show_error
from transcription.ui.components.primitives import destructive_button
from transcription.ui.components.primitives import render_empty_state
from transcription.ui.components.primitives import section_header_row
from transcription.ui.components.table.people import PersonTableRow, render_people_table
@@ -241,11 +242,12 @@ def register_page() -> None: # noqa: PLR0915
on_click=lambda: ui.navigate.to(f"/people/{person.id}/edit"),
icon="edit",
).classes("ui-btn-primary text-xs")
ui.button(
destructive_button(
"Delete",
on_click=lambda: ui.navigate.to(f"/people/{person.id}/delete"),
icon="delete",
).props("outlined color=negative text-xs")
extra_classes="text-xs",
)
with ui.grid().classes("w-full grid-cols-12 gap-4"):
with ui.column().classes("col-span-12 lg:col-span-4"):
@@ -467,9 +469,10 @@ def register_page() -> None: # noqa: PLR0915
ui.navigate.to("/people")
with ui.row().classes("w-full items-center gap-2 mt-2"):
ui.button(
destructive_button(
"Delete person permanently",
on_click=submit_delete,
icon="delete_forever",
).props("unelevated color=negative")
variant="solid",
)
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/people/{person.id}"), icon="arrow_back").props("flat")
+20
View File
@@ -209,6 +209,26 @@ input:focus-visible,
color: var(--theme-secondary);
}
.ui-page-header {
display: flex;
flex-direction: column;
gap: 0;
width: 100%;
}
.ui-page-title {
color: var(--theme-text);
font-family: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Palatino, serif;
font-size: 1.75rem;
font-weight: 700;
line-height: 1.2;
}
.ui-page-subtitle {
color: var(--theme-text-muted);
font-size: 0.75rem;
}
.ui-table {
border: 1px solid var(--theme-border);
color: var(--theme-text);
+20 -22
View File
@@ -1,35 +1,33 @@
from nicegui import ui
# Exact Palette & Typography Constants from UI Design Specification
# Runtime bridge for Quasar color slots. Visual ownership remains in theme.css tokens/classes.
THEME_COLORS = {
"primary": "#2D5A4C", # Library Green
"secondary": "#E2C7A8", # Aged Sepia
"positive": "#2D5A4C",
"accent": "#E2C7A8",
"dark": "#2B2D2C", # Matte Slate
"negative": "#A83232",
"primary": "#5e6572",
"secondary": "#7d98a1",
"accent": "#a9b4c2",
"dark": "#1c2321",
"positive": "#7d98a1",
"negative": "#1c2321",
"info": "#7d98a1",
"warning": "#a9b4c2",
}
# Typography Macros
STYLE_SERIF_HEADER = "font-family: 'Georgia', 'Times New Roman', serif;"
STYLE_SANS_BODY = "font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;"
_THEME_APPLIED = False
def apply_archival_theme() -> None:
"""Configures global NiceGUI theme variables and injects custom global styles."""
ui.colors(**THEME_COLORS)
"""Apply runtime color slots once; visual styling is defined in theme.css."""
global _THEME_APPLIED
if _THEME_APPLIED:
return
# Global body rules: Archival Cream (#FAF9F6) background, Iron Ink (#333333) text
ui.query("body").style(
f"background-color: #FAF9F6; color: #333333; {STYLE_SANS_BODY}"
)
ui.colors(**THEME_COLORS)
_THEME_APPLIED = True
def page_header(title: str, subtitle: str | None = None) -> None:
"""Standardized page title component with Archival Serif styling."""
with ui.column().classes("gap-0 pb-2 border-b border-[#6B6A65]/30 w-full"):
ui.label(title).style(
f"{STYLE_SERIF_HEADER} font-size: 1.75rem; font-weight: 700; color: #333333;"
)
"""Standardized page title component using semantic classes."""
with ui.column().classes("ui-page-header"):
ui.label(title).classes("ui-page-title")
if subtitle:
ui.label(subtitle).classes("text-xs text-[#6B6A65]")
ui.label(subtitle).classes("ui-page-subtitle")