def home(request: Request):
return FileResponse(os.path.join(FRONTEND_DIR, "hosts.html"))
-# Main CSS
+# CSS variables
@app.get("/css/variables.css")
def home(request: Request):
return FileResponse(os.path.join(FRONTEND_DIR, "css/variables.css"))
+
+# CSS Layoyt
+@app.get("/css/layout.css")
+def home(request: Request):
+ return FileResponse(os.path.join(FRONTEND_DIR, "css/layout.css"))
/* ================================
- Global layout
- ================================ */
-body {
- font-family: sans-serif;
- margin: 20px;
- padding-top: 60px; /* space for fixed topbar */
- background-color: var(--bg-light);
-}
-
-/* ================================
- Topbar (pfSense full-width header)
- ================================ */
-.topbar {
- width: 100%;
- background: var(--bg-dark);
- padding: 0;
- display: flex;
- align-items: center;
- border-bottom: 3px solid var(--accent);
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1000;
-}
-
-.topbar-inner {
- width: 100%;
- margin: 0 auto;
- padding: 14px 26px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- box-sizing: border-box;
-}
-
-.logo {
- display: flex;
- align-items: center;
- gap: 14px;
-}
-
-.logo span {
- font-size: 1.6rem;
- font-weight: 600;
- color: var(--text-light);
- letter-spacing: 0.5px;
-}
-
-.logo svg {
- filter: drop-shadow(0 0 2px rgba(0,0,0,0.6));
-}
-
-/* ================================
- Page frame (section header)
- ================================ */
-.page-frame {
- background-color: var(--bg-frame);
- border-left: 4px solid var(--accent);
- padding: 6px 14px;
- margin-bottom: 25px;
- box-shadow: 0 3px 6px rgba(0,0,0,0.18);
-}
-
-.page-frame h2 {
- margin: 0;
- line-height: 1.2;
-}
-
-.section-title {
- font-size: 1.2rem;
- font-weight: 600;
- color: var(--text-dark);
-}
-
-.frame-row {
- display: flex;
- align-items: center;
- gap: 12px;
-}
-
-.frame-row h2 {
- margin-right: auto;
-}
-
-/* ================================
- Typography
- ================================ */
-h1 {
- margin-bottom: 20px;
-}
-
-/* ================================
- Table styling
- ================================ */
-table {
- border-collapse: collapse;
- width: 100%;
- background-color: white;
- box-shadow: 0 3px 6px rgba(0,0,0,0.18);
-}
-
-th, td {
- border: 1px solid var(--border-light);
- padding: 10px 12px;
- text-align: left;
- vertical-align: middle;
-}
-
-th {
- background-color: #eee;
- font-weight: bold;
- cursor: pointer;
- user-select: none;
- position: relative;
-}
-
-th:hover::after {
- content: "";
- position: absolute;
- left: 0;
- right: 0;
- bottom: 0;
- height: 2px;
- background: var(--accent);
-}
-
-.sort-arrow {
- margin-left: 6px;
- font-size: 12px;
- opacity: 0.6;
- display: inline-block;
- width: 12px; /* prevents column shifting */
- text-align: center;
-}
-
-/* ================================
- Action icons column
+ Action icons column (specific to hosts)
================================ */
td.actions {
white-space: nowrap;
}
/* ================================
- Logout button (pfSense style)
- ================================ */
-.logout-btn {
- background-color: var(--accent);
- color: white;
- border: none;
- padding: 6px 14px;
- font-size: 0.95rem;
- font-weight: 600;
- border-radius: 4px;
- cursor: pointer;
- transition: background 0.2s ease;
- box-shadow: 0 2px 4px rgba(0,0,0,0.15);
-}
-
-.logout-btn:hover {
- background-color: var(--accent-hover);
-}
-
-/* ================================
- Add-host button (pfSense style)
- ================================ */
-.add-host-btn {
- background-color: var(--accent);
- color: white;
- border: none;
- padding: 6px 14px;
- font-size: 0.95rem;
- font-weight: 600;
- border-radius: 4px;
- cursor: pointer;
- transition: background 0.2s ease;
- box-shadow: 0 2px 4px rgba(0,0,0,0.15);
-}
-
-.add-host-btn:hover {
- background-color: var(--accent-hover);
-}
-
-/* ================================
- Search bar
+ Search bar (specific to hosts)
================================ */
.search-bar {
width: 250px;
border-radius: 6px;
font-size: 14px;
}
-
-/* ================================
- Toast notification
- ================================ */
-.toast {
- position: fixed;
- top: 20px;
- right: 20px;
- background: #333;
- color: white;
- padding: 12px 18px;
- border-radius: 6px;
- opacity: 0;
- pointer-events: none;
- transition: opacity 0.4s ease;
- font-size: 14px;
- z-index: 9999;
-}
-
-.toast.show {
- opacity: 1;
-}
-
-/* ================================
- Modal overlay
- ================================ */
-.modal {
- display: none;
- position: fixed;
- z-index: 2000;
- inset: 0;
- background: rgba(0,0,0,0.45);
- justify-content: center;
- align-items: center;
-}
-
-/* ================================
- Modal window
- ================================ */
-.modal-content {
- background: #f4f4f4;
- padding: 20px 24px;
- border-left: 4px solid var(--accent);
- border-radius: 6px;
- width: 320px;
- box-shadow: 0 4px 12px rgba(0,0,0,0.25);
-}
-
-.modal-content h3 {
- margin: 0 0 14px 0;
- font-size: 1.2rem;
- color: var(--text-dark);
-}
-
-.modal-content label {
- display: block;
- margin-top: 10px;
- font-weight: 600;
- color: #333;
-}
-
-.modal-content input[type="text"] {
- width: 100%;
- padding: 6px;
- margin-top: 4px;
- border: 1px solid #bbb;
- border-radius: 4px;
-}
-
-/* ================================
- Modal buttons
- ================================ */
-.modal-buttons {
- display: flex;
- justify-content: flex-end;
- gap: 10px;
- margin-top: 18px;
-}
-
-.cancel-btn {
- background: #ccc;
- border: none;
- padding: 6px 12px;
- border-radius: 4px;
- cursor: pointer;
-}
-
-.save-btn {
- background: var(--accent);
- color: white;
- border: none;
- padding: 6px 14px;
- border-radius: 4px;
- cursor: pointer;
-}
-
-.save-btn:hover {
- background: var(--accent-hover);
-}
-
-/* ================================
- SVG icons
- ================================ */
-svg {
- display: block;
- pointer-events: none;
-}
\ No newline at end of file
--- /dev/null
+/* ================================
+ Global layout
+ ================================ */
+body {
+ font-family: sans-serif;
+ margin: 20px;
+ padding-top: 60px; /* space for fixed topbar */
+ background-color: var(--bg-light);
+}
+
+/* ================================
+ Topbar (pfSense full-width header)
+ ================================ */
+.topbar {
+ width: 100%;
+ background: var(--bg-dark);
+ padding: 0;
+ display: flex;
+ align-items: center;
+ border-bottom: 3px solid var(--accent);
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1000;
+}
+
+.topbar-inner {
+ width: 100%;
+ margin: 0 auto;
+ padding: 14px 26px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ box-sizing: border-box;
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+}
+
+.logo span {
+ font-size: 1.6rem;
+ font-weight: 600;
+ color: var(--text-light);
+ letter-spacing: 0.5px;
+}
+
+.logo svg {
+ filter: drop-shadow(0 0 2px rgba(0,0,0,0.6));
+}
+
+/* ================================
+ Page frame (section header)
+ ================================ */
+.page-frame {
+ background-color: var(--bg-frame);
+ border-left: 4px solid var(--accent);
+ padding: 6px 14px;
+ margin-bottom: 25px;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.18);
+}
+
+.page-frame h2 {
+ margin: 0;
+ line-height: 1.2;
+}
+
+.section-title {
+ font-size: 1.2rem;
+ font-weight: 600;
+ color: var(--text-dark);
+}
+
+.frame-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.frame-row h2 {
+ margin-right: auto;
+}
+
+/* ================================
+ Typography
+ ================================ */
+h1 {
+ margin-bottom: 20px;
+}
+
+/* ================================
+ Table styling
+ ================================ */
+table {
+ border-collapse: collapse;
+ width: 100%;
+ background-color: white;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.18);
+}
+
+th, td {
+ border: 1px solid var(--border-light);
+ padding: 10px 12px;
+ text-align: left;
+ vertical-align: middle;
+}
+
+th {
+ background-color: #eee;
+ font-weight: bold;
+ cursor: pointer;
+ user-select: none;
+ position: relative;
+}
+
+th:hover::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 2px;
+ background: var(--accent);
+}
+
+.sort-arrow {
+ margin-left: 6px;
+ font-size: 12px;
+ opacity: 0.6;
+ display: inline-block;
+ width: 12px;
+ text-align: center;
+}
+
+/* ================================
+ Buttons (pfSense style)
+ ================================ */
+.btn-primary {
+ background-color: var(--accent);
+ color: white;
+ border: none;
+ padding: 6px 14px;
+ font-size: 0.95rem;
+ font-weight: 600;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background 0.2s ease;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.15);
+}
+
+.btn-primary:hover {
+ background-color: var(--accent-hover);
+}
+
+/* ================================
+ Toast notification
+ ================================ */
+.toast {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: #333;
+ color: white;
+ padding: 12px 18px;
+ border-radius: 6px;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.4s ease;
+ font-size: 14px;
+ z-index: 9999;
+}
+
+.toast.show {
+ opacity: 1;
+}
+
+/* ================================
+ Modal overlay
+ ================================ */
+.modal {
+ display: none;
+ position: fixed;
+ z-index: 2000;
+ inset: 0;
+ background: rgba(0,0,0,0.45);
+ justify-content: center;
+ align-items: center;
+}
+
+/* ================================
+ Modal window
+ ================================ */
+.modal-content {
+ background: #f4f4f4;
+ padding: 20px 24px;
+ border-left: 4px solid var(--accent);
+ border-radius: 6px;
+ width: 320px;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.25);
+}
+
+.modal-content h3 {
+ margin: 0 0 14px 0;
+ font-size: 1.2rem;
+ color: var(--text-dark);
+}
+
+.modal-content label {
+ display: block;
+ margin-top: 10px;
+ font-weight: 600;
+ color: #333;
+}
+
+.modal-content input[type="text"] {
+ width: 100%;
+ padding: 6px;
+ margin-top: 4px;
+ border: 1px solid #bbb;
+ border-radius: 4px;
+}
+
+/* ================================
+ Modal buttons
+ ================================ */
+.modal-buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ margin-top: 18px;
+}
+
+.cancel-btn {
+ background: #ccc;
+ border: none;
+ padding: 6px 12px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.save-btn {
+ background: var(--accent);
+ color: white;
+ border: none;
+ padding: 6px 14px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.save-btn:hover {
+ background: var(--accent-hover);
+}
+
+/* ================================
+ SVG icons
+ ================================ */
+svg {
+ display: block;
+ pointer-events: none;
+}
-body {
- margin: 0;
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
- background: #f3f6fb;
-}
-
+/* ================================
+ Login page wrapper
+ ================================ */
.login-wrapper {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
+ background: var(--bg-light);
}
+/* ================================
+ Login box
+ ================================ */
.login-box {
- background: #ffffff;
+ background: white;
padding: 24px 28px;
border-radius: 8px;
- box-shadow: 0 4px 14px rgba(0,0,0,0.12);
width: 320px;
+ box-shadow: 0 4px 14px var(--shadow-soft);
+ border-left: 4px solid var(--accent);
}
+/* ================================
+ Logo / title
+ ================================ */
.login-logo {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
- color: #4da3ff;
+ color: var(--accent);
font-weight: 600;
}
.login-box h2 {
margin: 0 0 16px;
font-size: 1.2rem;
+ color: var(--text-dark);
}
-label {
+/* ================================
+ Labels & inputs
+ ================================ */
+.login-box label {
display: block;
font-size: 0.85rem;
margin-bottom: 4px;
- color: #444;
+ color: var(--text-dark);
}
-input[type="text"],
-input[type="password"] {
+.login-box input[type="text"],
+.login-box input[type="password"] {
width: 100%;
padding: 7px 10px;
border-radius: 4px;
- border: 1px solid #ccc;
+ border: 1px solid var(--border-light);
font-size: 0.95rem;
margin-bottom: 12px;
box-sizing: border-box;
+ background: var(--bg-light);
}
-.login-btn {
+/* ================================
+ Login button
+ ================================ */
+.btn-primary.login-btn {
width: 100%;
- background-color: #4da3ff;
- color: white;
- border: none;
padding: 8px 0;
- font-size: 0.95rem;
- font-weight: 600;
- border-radius: 4px;
- cursor: pointer;
- transition: background 0.2s ease;
+ box-shadow: none;
}
-.login-btn:hover {
- background-color: #1f8bff;
+.btn-primary.login-btn:hover {
+ box-shadow: none;
}
+/* ================================
+ Error message
+ ================================ */
.login-error {
margin-top: 10px;
font-size: 0.85rem;
--text-dark: #222;
--border-light: #ccc;
+ --border-dark: #999;
+
+ --shadow-soft: rgba(0, 0, 0, 0.12);
+ --shadow-strong: rgba(0, 0, 0, 0.25);
}
<meta charset="UTF-8">
<title>Network Manager</title>
<link rel="stylesheet" href="css/variables.css">
+ <link rel="stylesheet" href="css/layout.css">
<link rel="stylesheet" href="css/hosts.css">
</head>
<body>
<span>Network Manager</span>
</div>
- <button id="logoutBtn" class="logout-btn">Logout</button>
+ <button id="logoutBtn" class="btn-primary">Logout</button>
</div>
</header>
class="search-bar"
/>
- <button class="add-host-btn" onclick="openAddHostModal()">
+ <button class="btn-primary" onclick="openAddHostModal()">
+ Add Host
</button>
<meta charset="UTF-8">
<title>Network Manager - Login</title>
<link rel="stylesheet" href="css/variables.css">
+ <link rel="stylesheet" href="css/layout.css">
<link rel="stylesheet" href="css/login.css">
</head>
<body>
<label for="password">Password</label>
<input type="password" id="password" required>
- <button type="submit" class="login-btn">Entra</button>
+ <button type="submit" class="btn-primary login-btn">Entra</button>
</form>
<div id="loginError" class="login-error"></div>