Spaces:
Paused
Paused
Download src/datathon/utils/styles.py from inria-chile/llaca-datathon: direct link, hf CLI and curl.
- Browser
- Download file 4.35 kB
-
https://huggingface.co/spaces/inria-chile/llaca-datathon/resolve/main/src/datathon/utils/styles.py
- Command line
-
hf download hf://spaces/inria-chile/llaca-datathon/src/datathon/utils/styles.py
-
curl -L -o styles.py https://huggingface.co/spaces/inria-chile/llaca-datathon/resolve/main/src/datathon/utils/styles.py
4.35 kB
| import base64 | |
| from functools import lru_cache | |
| from pathlib import Path | |
| CSS_PATH = Path(__file__).resolve().parent.parent / "ui"/"assets" / "style.css" | |
| # Decorative app-wide background. A downscaled WebP keeps the inlined payload | |
| # small (~300 KB) while the original PNG stays as the master asset. | |
| FONDO_PATH = ( | |
| Path(__file__).resolve().parent.parent / "ui" / "assets" / "datathon_fondo_bg.webp" | |
| ) | |
| FONDO_OPACITY = 0.3 | |
| # Palette tokens — the colours themselves live in style.css (:root). Keeping | |
| # the token names here lets the inline status banners follow the theme. | |
| COLOR_ERROR = "var(--color-danger-bg)" | |
| COLOR_SUCCESS = "var(--color-success-bg)" | |
| COLOR_ON_ACCENT = "var(--color-white)" | |
| def is_error_message(message: str) -> bool: | |
| """True when a UI message is an error, whatever its display language. | |
| Participant lifecycle messages are produced through the i18n dictionary, | |
| whose ES errors start with "Error" and PT errors with "Erro". | |
| """ | |
| return message.startswith(("Error", "Erro")) | |
| def get_fondo_css() -> str: | |
| """CSS del fondo decorativo global (``datathon_fondo``). | |
| La imagen se embebe como data URI base64 (mismo criterio que el logo del | |
| sidebar) para no depender de una ruta servida estáticamente. Un | |
| pseudo-elemento ``::before`` porta la opacidad, ya que CSS no permite fijar | |
| la opacidad de un ``background-image`` directamente. El contenedor raíz de | |
| Gradio establece su propio contexto de apilamiento (``isolation``) para que | |
| el fondo quede por detrás del contenido. | |
| """ | |
| try: | |
| data = base64.b64encode(FONDO_PATH.read_bytes()).decode("ascii") | |
| except OSError: | |
| return "" | |
| return ( | |
| ".gradio-container{position:relative;isolation:isolate;}" | |
| # position:fixed (not background-attachment:fixed) so the background is | |
| # anchored to the viewport and cover is computed against it; works on | |
| # mobile browsers, where background-attachment:fixed is unreliable. | |
| ".gradio-container::before{" | |
| "content:'';position:fixed;inset:0;z-index:-1;" | |
| "background:url('data:image/webp;base64," | |
| f"{data}" | |
| "') center/cover no-repeat;" | |
| f"opacity:{FONDO_OPACITY};pointer-events:none;" | |
| "}" | |
| ) | |
| def get_css() -> str: | |
| """Devuelve el CSS global para inyectarlo en el <head> de la app. | |
| Pasar esta cadena a ``gr.mount_gradio_app(css=...)`` hace que Gradio la | |
| aplique antes del primer render, evitando el flash de contenido sin estilo. | |
| """ | |
| base = CSS_PATH.read_text(encoding="utf-8") if CSS_PATH.exists() else "" | |
| return base + get_fondo_css() | |
| # Inria Sans is the brand typeface for the UI (see style.css, which maps the | |
| # theme's `--font` variable to it). The stylesheet is injected in the document | |
| # head through `gr.mount_gradio_app(head=...)`; the preconnect hints shave the | |
| # TLS handshake off the font request. | |
| # | |
| # `display=optional` (not `swap`): the web font is fetched asynchronously, so a | |
| # late arrival under `swap` re-wraps text after first paint. In the HF Space | |
| # iframe that made the header row (banner + login/language controls) reflow and | |
| # the login button jump; a manual resize recomputed the stale layout. With | |
| # `optional` the layout never waits for the font and never swaps after paint | |
| # (fallback on the first uncached load, Inria Sans on later cached loads), which | |
| # removes the layout shift. | |
| INRIA_SANS_URL = ( | |
| "https://fonts.googleapis.com/css2" | |
| "?family=Inria+Sans:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700" | |
| "&display=optional" | |
| ) | |
| def get_head() -> str: | |
| """Devuelve el HTML extra para el <head> (Google Fonts de Inria Sans).""" | |
| return ( | |
| '<link rel="preconnect" href="https://fonts.googleapis.com">' | |
| '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>' | |
| f'<link rel="stylesheet" href="{INRIA_SANS_URL}">' | |
| ) | |
| def status_banner_html(message: str, is_error: bool) -> str: | |
| """Envuelve un mensaje de estado. Errores con fondo rojo, éxito con verde. | |
| La caja es cuadrada (sin radio) con borde negro de 1px. | |
| """ | |
| bg = COLOR_ERROR if is_error else COLOR_SUCCESS | |
| return ( | |
| f"<div style='background-color:{bg};color:{COLOR_ON_ACCENT};" | |
| f"padding:8px;border:1px solid #000000;border-radius:0;'>" | |
| f"{message}</div>" | |
| ) | |