lab-setup / templates /base.html
iurbinah's picture
Initial lab-setup portal: Flask + dark theme + oTree session page
e2c12e7
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}Lab Portal{% endblock %}</title>
<link rel="icon" href="{{ url_for('static', filename='favicon.svg') }}" type="image/svg+xml" />
<style>
/* ── CSS variables (dark theme – matches csv-viewer) ── */
:root {
--bg: #0e0e10;
--surface: #1a1a2e;
--surface2: #22223a;
--border: #2a2a4a;
--text: #e2e2e8;
--text-dim: #9090a8;
--accent: #6c63ff;
--accent-h: #8b83ff;
--danger: #ff6b6b;
--success: #51cf66;
--sidebar-w: 220px;
--topbar-h: 0px;
}
/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
display: flex;
}
/* ── Sidebar ── */
.sidebar {
width: var(--sidebar-w);
min-height: 100vh;
background: var(--surface);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
padding: 1.25rem 0;
flex-shrink: 0;
}
.sidebar-brand {
padding: 0 1.25rem 1.25rem;
font-size: 1.1rem;
font-weight: 700;
letter-spacing: .03em;
color: var(--accent);
border-bottom: 1px solid var(--border);
margin-bottom: .75rem;
}
.sidebar nav { flex: 1; display: flex; flex-direction: column; gap: 2px; padding: 0 .5rem; }
.sidebar a {
display: flex;
align-items: center;
gap: .65rem;
padding: .6rem .85rem;
border-radius: 8px;
color: var(--text-dim);
text-decoration: none;
font-size: .92rem;
transition: background .15s, color .15s;
}
.sidebar a:hover { background: var(--surface2); color: var(--text); }
.sidebar a.active { background: var(--accent); color: #fff; }
.sidebar a .icon { font-size: 1.15rem; }
.sidebar-footer {
padding: .75rem 1.25rem 0;
border-top: 1px solid var(--border);
margin-top: auto;
}
.sidebar-footer a {
color: var(--text-dim);
text-decoration: none;
font-size: .82rem;
}
.sidebar-footer a:hover { color: var(--danger); }
/* ── Main content ── */
.main {
flex: 1;
display: flex;
flex-direction: column;
min-height: 100vh;
overflow: hidden;
}
.page-header {
padding: 1.25rem 2rem;
border-bottom: 1px solid var(--border);
background: var(--surface);
}
.page-header h1 {
font-size: 1.25rem;
font-weight: 600;
}
.page-header p {
color: var(--text-dim);
font-size: .85rem;
margin-top: .25rem;
}
.page-body {
flex: 1;
padding: 2rem;
overflow-y: auto;
}
/* ── Utility classes ── */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
}
.btn {
display: inline-flex;
align-items: center;
gap: .5rem;
padding: .55rem 1.3rem;
border-radius: 8px;
font-size: .9rem;
font-weight: 600;
border: none;
cursor: pointer;
text-decoration: none;
transition: background .15s, transform .1s;
}
.btn:active { transform: scale(.97); }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-h); }
.btn-outline {
background: transparent;
border: 1px solid var(--border);
color: var(--text);
}
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }
/* ── Responsive ── */
@media (max-width: 768px) {
.sidebar { width: 56px; }
.sidebar-brand, .sidebar a span, .sidebar-footer span { display: none; }
.sidebar a { justify-content: center; padding: .6rem; }
.page-body { padding: 1rem; }
}
/* ── Block: page-specific CSS injected here ── */
{% block extra_css %}{% endblock %}
</style>
</head>
<body>
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-brand">Lab Portal</div>
<nav>
{% for endpoint, icon, label in sidebar_pages %}
<a href="{{ url_for(endpoint) }}"
class="{{ 'active' if active_page == endpoint else '' }}">
<span class="icon">{{ icon }}</span>
<span>{{ label }}</span>
</a>
{% endfor %}
</nav>
<div class="sidebar-footer">
<a href="{{ url_for('logout') }}"><span>Sign out</span></a>
</div>
</aside>
<!-- Main content area -->
<div class="main">
{% block header %}{% endblock %}
<div class="page-body">
{% block content %}{% endblock %}
</div>
</div>
{% block extra_js %}{% endblock %}
</body>
</html>