diff --git a/src/gui/_home.py b/src/gui/_home.py index 4780657..234a6fc 100644 --- a/src/gui/_home.py +++ b/src/gui/_home.py @@ -172,7 +172,7 @@ def _home_page() -> None: from src.i18n import t from pathlib import Path as _Path - _ICON_PATH = str(_Path(__file__).parent / "assets" / "datatools_icon.svg") + _ICON_PATH = str(_Path(__file__).parent / "assets" / "datatools_icon_256.png") st.set_page_config( page_title=t("home.page_title"), page_icon=_ICON_PATH, @@ -182,17 +182,22 @@ def _home_page() -> None: render_sticky_footer() import html as _html - # Page header — h1 + body subtitle on the left, privacy pill on - # the right (mockup §page-header). Rendered as a single HTML block - # so the title/subtitle/pill share one flex row; ``st.title`` + - # ``st.caption`` + ``st.divider`` would stack vertically and lose - # the right-aligned pill. Bottom border replaces the explicit - # ``st.divider`` that used to sit below the caption. + # Page header — brand block (D icon + "UNALOGIX" eyebrow over + # "DataTools" wordmark + tagline) on the left, privacy pill on + # the right. Matches the sidebar brand chip scaled up for the + # hero. Bottom border replaces the explicit ``st.divider`` that + # used to sit below the caption. privacy_label = _html.escape(t("home.privacy_pill")) st.markdown( '
' - '
' - f'

{_html.escape(t("home.title"))}

' + '
' + '
' + '
D
' + '
' + 'UNALOGIX' + '

DataTools

' + '
' + '
' f'

{_html.escape(t("home.caption"))}

' '
' '' diff --git a/src/gui/assets/datatools_icon_256.png b/src/gui/assets/datatools_icon_256.png new file mode 100644 index 0000000..497d260 Binary files /dev/null and b/src/gui/assets/datatools_icon_256.png differ diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py index 4f0e8a2..2e05e93 100644 --- a/src/gui/components/_legacy.py +++ b/src/gui/components/_legacy.py @@ -267,14 +267,20 @@ body, .stApp { margin: 0 !important; } -/* Nav items — tight padding so the menu lists feel dense and don't - waste vertical space. */ +/* Nav items match the sticky-footer Help/Close button style: ink- + secondary text, transparent surface, soft hover tint, no border or + active-state pill. Sizes line up with ``.datatools-footer-btn`` + (13px / 500 / 1.3 line-height, 5px×10px padding, 8px icon gap) so + the sidebar and footer feel like the same family. */ [data-testid="stSidebarNav"] a[data-testid="stSidebarNavLink"], [data-testid="stSidebarNav"] [data-testid="stSidebarNavLinkContainer"] a { color: var(--ink-secondary) !important; - font-size: 13.5px !important; - line-height: 1.25 !important; - padding: 4px 10px !important; + font-size: 13px !important; + font-weight: 500 !important; + line-height: 1.3 !important; + padding: 5px 10px !important; + gap: 8px !important; + border: none !important; border-radius: var(--r-sm) !important; transition: background 0.12s ease, color 0.12s ease; } @@ -291,13 +297,14 @@ body, .stApp { background: rgba(0,0,0,0.04) !important; color: var(--ink) !important; } -/* Active nav item — white pill with subtle shadow. Streamlit marks the - active anchor with ``aria-current="page"``. */ +/* Active item — soft hover-tint background + ink text + heavier + weight. No white pill, no shadow. Mirrors the footer buttons, + which carry no special "active" treatment. */ [data-testid="stSidebarNav"] a[aria-current="page"] { - background: var(--surface) !important; + background: rgba(0,0,0,0.04) !important; color: var(--ink) !important; - font-weight: 500 !important; - box-shadow: 0 1px 2px rgba(28,25,23,0.04) !important; + font-weight: 600 !important; + box-shadow: none !important; } /* Inline + block code → mono with subtle accent chip. theme.py owns @@ -577,22 +584,89 @@ div[data-testid="stContainer"][data-border="true"] { overflow: hidden !important; } -/* ---------- Page header (title + subtitle + privacy pill) ---------- */ +/* ---------- Page header (brand block + privacy pill) ---------- */ .dt-page-header { display: flex; - align-items: flex-end; + align-items: center; justify-content: space-between; gap: 24px; margin: 0 0 24px; padding-bottom: 22px; border-bottom: 1px solid var(--border); } -.dt-page-header h1 { margin: 0 !important; } +/* The brand block stacks two pieces vertically: the D-chip + words + row up top, then the tagline beneath. The D mark vertically + centres with the words column (eyebrow + wordmark), exactly like + the sidebar chip. */ +.dt-page-brand { + display: flex; + flex-direction: column; + gap: 8px; +} +.dt-page-brand-row { + display: flex; + align-items: center; + gap: 18px; +} +.dt-page-brand-words { + display: flex; + flex-direction: column; + gap: 2px; + line-height: 1; +} +/* Streamlit wraps the h1 in an emotion-cache div that adds ~3px top + padding + ~8px bottom margin. Flatten every descendant so the + eyebrow + wordmark stack hugs the chip height. */ +.dt-page-brand-words *, +.dt-page-brand-words > div { + margin: 0 !important; + padding: 0 !important; +} +.dt-page-brand-words .dt-page-wordmark { + line-height: 1 !important; +} +/* Same "Letter D (sans)" wordmark as the sidebar chip and favicon + — scaled up to hero size. Ink ground, cream D, Geist 700, -0.04em + tracking. */ +.dt-page-brand-mark { + width: 56px; + height: 56px; + border-radius: 14px; + background: var(--ink); + color: var(--accent-fill); + display: inline-flex; + align-items: center; + justify-content: center; + font-family: var(--font-sans); + font-weight: 700; + font-size: 32px; + letter-spacing: -0.04em; + line-height: 1; + flex-shrink: 0; +} +.dt-page-eyebrow { + font-family: var(--font-sans) !important; + font-size: 11.5px; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--ink-tertiary); + line-height: 1.2; +} +.dt-page-wordmark { + margin: 0 !important; + font-family: var(--font-sans) !important; + font-weight: 600 !important; + font-size: 32px !important; + letter-spacing: -0.035em !important; + line-height: 1.1 !important; + color: var(--ink) !important; +} .dt-page-header .dt-page-subtitle { - margin: 6px 0 0; + margin: 4px 0 0; color: var(--ink-secondary) !important; font-size: 14px; - line-height: 1.55; + line-height: 1.5; } .dt-privacy-pill { display: inline-flex; @@ -740,9 +814,9 @@ div[data-testid="stContainer"][data-border="true"] { .dt-finding-group-head:hover { background: var(--accent-fill); } -/* Chevron lives on the right of the head, rotates to indicate state. */ +/* Chevron leads the head as the first flex item; rotates 90° to + indicate expanded state. */ .dt-finding-group-chevron { - margin-left: 8px; color: var(--ink-tertiary); font-family: "Material Symbols Outlined" !important; font-size: 20px !important; @@ -750,6 +824,7 @@ div[data-testid="stContainer"][data-border="true"] { line-height: 1 !important; transition: transform 0.15s ease; flex-shrink: 0; + margin-right: -2px; } .dt-finding-group-head[data-dt-collapsed="false"] .dt-finding-group-chevron { transform: rotate(90deg); @@ -2843,12 +2918,15 @@ def render_findings_panel( f'{n} {label}' ) + # Chevron leads the head — clicking the row toggles + # ``data-dt-collapsed``. ``chevron_right`` (▶) is the collapsed + # rest state; CSS rotates it 90° to point down (▼) when expanded. head_html = ( '
' + 'chevron_right' f'' f'{_html.escape(header)}' f'
{pills_html}
' - 'chevron_right' '
' ) st.markdown(head_html, unsafe_allow_html=True) @@ -2911,10 +2989,13 @@ def _render_finding_row_v2(f, *, row_key: str) -> None: meta_html = " · ".join(meta_parts) # Action button moved to the LEFT of the description per UX - # feedback: ``[icon] [Open →] [description]`` — the action - # is now the prominent affordance in the row, with the description - # taking the wide remaining column. - col_icon, col_action, col_body = st.columns([0.4, 1.8, 8]) + # feedback: ``[icon] [ →] [description]`` — the action is + # the prominent affordance in the row, with the description taking + # the wide remaining column. Tight action-column ratio (1.4) plus + # ``width="content"`` on the button below keeps the link + # left-justified against the icon with minimal surrounding + # whitespace. + col_icon, col_action, col_body = st.columns([0.4, 1.4, 8]) col_icon.markdown( f'
' @@ -2930,7 +3011,7 @@ def _render_finding_row_v2(f, *, row_key: str) -> None: f"{tool_label} →", key=f"_finding_open_{row_key}", type="tertiary", - width="stretch", + width="content", ): st.switch_page(page_slug) diff --git a/src/gui/pages/99_Close.py b/src/gui/pages/99_Close.py index a870ec2..9847350 100644 --- a/src/gui/pages/99_Close.py +++ b/src/gui/pages/99_Close.py @@ -21,7 +21,7 @@ from src.gui.components import hide_streamlit_chrome, shutdown_app from src.i18n import t from pathlib import Path as _Path -_ICON_PATH = str(_Path(__file__).parent.parent / "assets" / "datatools_icon.svg") +_ICON_PATH = str(_Path(__file__).parent.parent / "assets" / "datatools_icon_256.png") st.set_page_config( page_title=t("close_page.page_title"), page_icon=_ICON_PATH, diff --git a/src/gui/pages/_Activate.py b/src/gui/pages/_Activate.py index c0bb2b7..4fbf3db 100644 --- a/src/gui/pages/_Activate.py +++ b/src/gui/pages/_Activate.py @@ -27,7 +27,7 @@ from src.gui.components import ( from src.i18n import t from pathlib import Path as _Path -_ICON_PATH = str(_Path(__file__).parent.parent / "assets" / "datatools_icon.svg") +_ICON_PATH = str(_Path(__file__).parent.parent / "assets" / "datatools_icon_256.png") st.set_page_config( page_title=t("activation.page_title"), page_icon=_ICON_PATH,