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_app_shell
from transcription.ui.components.app_shell import render_navigation_header from transcription.ui.components.app_shell import render_navigation_header
from transcription.ui.components.document_panzoom import render_document_panzoom 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__ = [ __all__ = [
"NAV_ITEMS", "NAV_ITEMS",
"destructive_button",
"render_app_shell", "render_app_shell",
"render_document_panzoom", "render_document_panzoom",
"render_empty_state",
"render_navigation_header", "render_navigation_header",
"section_header_row",
] ]
@@ -17,3 +17,22 @@ def render_empty_state(message: str, *, italic: bool = False, extra_classes: str
if italic: if italic:
classes = f"{classes} italic" classes = f"{classes} italic"
ui.label(message).classes(f"{classes} {extra_classes}".strip()) 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 nicegui import ui
from transcription.ui.components.cards import archival_card 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 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.""" """Render documents table and open detail page when clicking a row."""
if not rows: if not rows:
with archival_card(extra_classes="p-8 text-center"): 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 return
build_table( build_table(
@@ -12,6 +12,7 @@ from uuid import UUID
from nicegui import ui from nicegui import ui
from transcription.ui.components.cards import archival_card from transcription.ui.components.cards import archival_card
from transcription.ui.components.primitives import render_empty_state
from .common import build_table 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.""" """Render jobs table and open a detail page when clicking a row."""
if not rows: if not rows:
with archival_card(extra_classes="p-8 text-center"): 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 return
build_table( build_table(
@@ -10,6 +10,7 @@ from uuid import UUID
from nicegui import ui from nicegui import ui
from transcription.ui.components.cards import archival_card 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 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.""" """Render people table and open detail page when clicking a row."""
if not rows: if not rows:
with archival_card(extra_classes="p-8 text-center"): 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 return
build_table( build_table(
@@ -10,6 +10,7 @@ from uuid import UUID
from nicegui import ui from nicegui import ui
from transcription.ui.components.cards import archival_card 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 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.""" """Render sources table and open detail page when clicking a row."""
if not rows: if not rows:
with archival_card(extra_classes="p-8 text-center"): 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 return
build_table( 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 archival_badge
from transcription.ui.components.data_display import metadata_row from transcription.ui.components.data_display import metadata_row
from transcription.ui.components.error_presenter import show_error 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 render_empty_state
from transcription.ui.components.primitives import section_header_row from transcription.ui.components.primitives import section_header_row
from transcription.ui.components.table.documents import DocumentTableRow 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"), on_click=lambda: ui.navigate.to(f"/documents/{document.id}/edit"),
icon="edit", icon="edit",
).classes("ui-btn-primary text-xs") ).classes("ui-btn-primary text-xs")
ui.button( destructive_button(
"Delete", "Delete",
on_click=lambda: ui.navigate.to(f"/documents/{document.id}/delete"), on_click=lambda: ui.navigate.to(f"/documents/{document.id}/delete"),
icon="delete", icon="delete",
).props("outlined color=negative text-xs") extra_classes="text-xs",
)
# High-Density Bento Grid Layout # High-Density Bento Grid Layout
with ui.grid().classes("w-full grid-cols-12 gap-4"): with ui.grid().classes("w-full grid-cols-12 gap-4"):
@@ -587,11 +589,12 @@ def register_page() -> None:
ui.navigate.to("/documents") ui.navigate.to("/documents")
with ui.row().classes("w-full items-center gap-2 mt-2"): with ui.row().classes("w-full items-center gap-2 mt-2"):
ui.button( destructive_button(
"Delete document permanently", "Delete document permanently",
on_click=submit_delete, on_click=submit_delete,
icon="delete_forever", 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( ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/documents/{document.id}"), icon="arrow_back").props(
"flat" "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 archival_badge
from transcription.ui.components.data_display import metadata_row from transcription.ui.components.data_display import metadata_row
from transcription.ui.components.error_presenter import show_error 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 render_empty_state
from transcription.ui.components.primitives import section_header_row from transcription.ui.components.primitives import section_header_row
from transcription.ui.components.table.jobs import render_jobs_table 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}") page_header(f"Job Record: {job.id}")
with ui.row().classes("items-center gap-2"): with ui.row().classes("items-center gap-2"):
archival_badge(job.status.value.upper()) archival_badge(job.status.value.upper())
ui.button( destructive_button(
"Delete Job", "Delete Job",
on_click=lambda: ui.navigate.to(f"/jobs/{job.id}/delete"), on_click=lambda: ui.navigate.to(f"/jobs/{job.id}/delete"),
icon="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 ui.grid().classes("w-full grid-cols-1 md:grid-cols-2 gap-4"):
with archival_card(title="Execution Logistics"): with archival_card(title="Execution Logistics"):
@@ -314,7 +316,10 @@ def register_page() -> None: # noqa: PLR0915
ui.navigate.to("/jobs") ui.navigate.to("/jobs")
with ui.row().classes("w-full items-center gap-2 mt-2"): 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( destructive_button(
"unelevated color=negative" "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") 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.cards import archival_card
from transcription.ui.components.data_display import metadata_row from transcription.ui.components.data_display import metadata_row
from transcription.ui.components.error_presenter import show_error 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 render_empty_state
from transcription.ui.components.primitives import section_header_row from transcription.ui.components.primitives import section_header_row
from transcription.ui.components.table.people import PersonTableRow, render_people_table 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"), on_click=lambda: ui.navigate.to(f"/people/{person.id}/edit"),
icon="edit", icon="edit",
).classes("ui-btn-primary text-xs") ).classes("ui-btn-primary text-xs")
ui.button( destructive_button(
"Delete", "Delete",
on_click=lambda: ui.navigate.to(f"/people/{person.id}/delete"), on_click=lambda: ui.navigate.to(f"/people/{person.id}/delete"),
icon="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.grid().classes("w-full grid-cols-12 gap-4"):
with ui.column().classes("col-span-12 lg:col-span-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") ui.navigate.to("/people")
with ui.row().classes("w-full items-center gap-2 mt-2"): with ui.row().classes("w-full items-center gap-2 mt-2"):
ui.button( destructive_button(
"Delete person permanently", "Delete person permanently",
on_click=submit_delete, on_click=submit_delete,
icon="delete_forever", 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") 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); 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 { .ui-table {
border: 1px solid var(--theme-border); border: 1px solid var(--theme-border);
color: var(--theme-text); color: var(--theme-text);
+20 -22
View File
@@ -1,35 +1,33 @@
from nicegui import ui 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 = { THEME_COLORS = {
"primary": "#2D5A4C", # Library Green "primary": "#5e6572",
"secondary": "#E2C7A8", # Aged Sepia "secondary": "#7d98a1",
"positive": "#2D5A4C", "accent": "#a9b4c2",
"accent": "#E2C7A8", "dark": "#1c2321",
"dark": "#2B2D2C", # Matte Slate "positive": "#7d98a1",
"negative": "#A83232", "negative": "#1c2321",
"info": "#7d98a1",
"warning": "#a9b4c2",
} }
# Typography Macros _THEME_APPLIED = False
STYLE_SERIF_HEADER = "font-family: 'Georgia', 'Times New Roman', serif;"
STYLE_SANS_BODY = "font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;"
def apply_archival_theme() -> None: def apply_archival_theme() -> None:
"""Configures global NiceGUI theme variables and injects custom global styles.""" """Apply runtime color slots once; visual styling is defined in theme.css."""
global _THEME_APPLIED
if _THEME_APPLIED:
return
ui.colors(**THEME_COLORS) ui.colors(**THEME_COLORS)
_THEME_APPLIED = True
# Global body rules: Archival Cream (#FAF9F6) background, Iron Ink (#333333) text
ui.query("body").style(
f"background-color: #FAF9F6; color: #333333; {STYLE_SANS_BODY}"
)
def page_header(title: str, subtitle: str | None = None) -> None: def page_header(title: str, subtitle: str | None = None) -> None:
"""Standardized page title component with Archival Serif styling.""" """Standardized page title component using semantic classes."""
with ui.column().classes("gap-0 pb-2 border-b border-[#6B6A65]/30 w-full"): with ui.column().classes("ui-page-header"):
ui.label(title).style( ui.label(title).classes("ui-page-title")
f"{STYLE_SERIF_HEADER} font-size: 1.75rem; font-weight: 700; color: #333333;"
)
if subtitle: if subtitle:
ui.label(subtitle).classes("text-xs text-[#6B6A65]") ui.label(subtitle).classes("ui-page-subtitle")