feat(statuspage): refactor shared styles and modernize components for consistent theming, spacing and APIs

This commit is contained in:
2025-12-23 09:26:37 +00:00
parent 891eb04d11
commit ed9728dd4a
24 changed files with 4177 additions and 3542 deletions

View File

@@ -1,5 +1,15 @@
# Changelog # Changelog
## 2025-12-23 - 1.1.0 - feat(statuspage)
refactor shared styles and modernize components for consistent theming, spacing and APIs
- Centralized styles: switch to a single sharedStyles module and extend design tokens (colors, spacing, shadows, borderRadius, easings, durations, accent colors).
- Component modernization: replace many destructured style imports with sharedStyles and convert numerous @property fields to 'accessor' for updated component APIs.
- Visual/layout updates: adjust typography, spacing scale, responsive rules and polish header/statusbar/footer/stats/incident components (improved gaps, paddings, status pills, loading skeletons, and button styles).
- Dependency bumps: updated @design.estate and @git.zone packages as well as @push.rocks dev deps and @types/node.
- Build/config changes: tsconfig.json flags removed (experimentalDecorators, useDefineForClassFields) and npmextra.json updated with registry/release config and new package keys.
- Docs and demos: expanded README content and added demo/test files (test/test.ts, test-shadcn-spacing.html) to showcase spacing and layout changes.
## 2024-06-27 - 1.0.74 - fix(core) ## 2024-06-27 - 1.0.74 - fix(core)
Updated font loading strategy in index.html for improved performance Updated font loading strategy in index.html for improved performance

View File

@@ -1,5 +1,5 @@
{ {
"gitzone": { "@git.zone/cli": {
"projectType": "wcc", "projectType": "wcc",
"module": { "module": {
"githost": "code.foss.global", "githost": "code.foss.global",
@@ -23,11 +23,17 @@
"UI", "UI",
"catalog" "catalog"
] ]
},
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
} }
}, },
"npmci": { "@ship.zone/szci": {
"npmGlobalTools": [], "npmGlobalTools": [],
"npmAccessLevel": "private",
"npmRegistryUrl": "verdaccio.lossless.one" "npmRegistryUrl": "verdaccio.lossless.one"
} }
} }

View File

@@ -15,19 +15,19 @@
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"@design.estate/dees-domtools": "^2.3.3", "@design.estate/dees-domtools": "^2.3.6",
"@design.estate/dees-element": "^2.0.45", "@design.estate/dees-element": "^2.1.3",
"@design.estate/dees-wcctools": "^1.1.0", "@design.estate/dees-wcctools": "^3.2.0",
"@uptime.link/interfaces": "^2.0.21" "@uptime.link/interfaces": "^2.0.21"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.6.4", "@git.zone/tsbuild": "^4.0.2",
"@git.zone/tsbundle": "^2.5.1", "@git.zone/tsbundle": "^2.6.3",
"@git.zone/tsrun": "^1.3.3", "@git.zone/tsrun": "^2.0.1",
"@git.zone/tswatch": "^2.1.2", "@git.zone/tswatch": "^2.3.13",
"@push.rocks/projectinfo": "^5.0.1", "@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/smartenv": "^5.0.0", "@push.rocks/smartenv": "^6.0.0",
"@types/node": "^24.0.7" "@types/node": "^25.0.3"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",

5310
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

515
readme.md
View File

@@ -1,205 +1,382 @@
# @uptime.link/statuspage # @uptime.link/statuspage
a catalog with webcomponents for uptimelink dashboard
## Install 🚀 **A powerful collection of web components for building stunning status pages** — because your users deserve to know what's happening, and you deserve to look good while telling them.
To install the `@uptime.link/statuspage` module, you can use npm. Make sure you have Node.js and npm installed, then run: Built with [Lit](https://lit.dev/) and TypeScript, these components are designed to be composable, customizable, and production-ready out of the box.
```sh ## Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
---
## ✨ Features
- 🎨 **Themeable** — Automatic light/dark mode support with CSS custom properties
- 📱 **Responsive** — Looks great on everything from mobile to ultrawide
- 🔌 **Composable** — Use components individually or build complete status pages
- 🎯 **Type-Safe** — Full TypeScript support with exported interfaces
-**Performant** — Lit-based components with minimal overhead
- 🎭 **Pre-built Pages** — Demo, all-green, outage, and maintenance page templates included
---
## 📦 Installation
```bash
npm install @uptime.link/statuspage npm install @uptime.link/statuspage
``` ```
## Usage Or with pnpm:
The `@uptime.link/statuspage` module provides a collection of web components to build a comprehensive status page for Uptime.link dashboards. This includes headers, status bars, asset selectors, status details, incident lists, and more. ```bash
pnpm add @uptime.link/statuspage
```
Heres a detailed guide on how to use each component in your TypeScript project with ESM syntax. We will walk through creating a complete status page using the provided components. ---
### Step-by-Step Example ## 🚀 Quick Start
1. **Setup Your Project:** Import the components and start building:
Ensure you have a TypeScript project setup. Here's a minimal `tsconfig.json` to get started: ```typescript
import '@uptime.link/statuspage';
```
```json Then use them in your HTML:
```html
<upl-statuspage-header></upl-statuspage-header>
<upl-statuspage-statusbar></upl-statuspage-statusbar>
<upl-statuspage-incidents></upl-statuspage-incidents>
<upl-statuspage-footer></upl-statuspage-footer>
```
---
## 🧩 Components
### `<upl-statuspage-header>`
The main navigation header with branding and action buttons.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `pageTitle` | `string` | `'Status'` | Title displayed in the header |
| `logoUrl` | `string` | `''` | URL to your logo image |
| `showReportButton` | `boolean` | `false` | Show "Report Incident" button |
| `showSubscribeButton` | `boolean` | `false` | Show "Subscribe" button |
**Events:**
- `reportNewIncident` — Fired when report button is clicked
- `statusSubscribe` — Fired when subscribe button is clicked
---
### `<upl-statuspage-pagetitle>`
A hero section with title and subtitle.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `pageTitle` | `string` | `'System Status'` | Main heading |
| `pageSubtitle` | `string` | `''` | Optional description text |
| `centered` | `boolean` | `false` | Center-align the content |
---
### `<upl-statuspage-statusbar>`
The overall status indicator — the heart of any status page.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `overallStatus` | `IOverallStatus` | — | Current system status object |
| `loading` | `boolean` | `false` | Show loading skeleton |
The status object supports: `operational`, `degraded`, `partial_outage`, `major_outage`, `maintenance`
---
### `<upl-statuspage-statsgrid>`
Key metrics at a glance — uptime, response time, incidents.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `currentStatus` | `string` | `'operational'` | Current status indicator |
| `uptime` | `number` | `100` | Uptime percentage |
| `avgResponseTime` | `number` | `0` | Average response time (ms) |
| `totalIncidents` | `number` | `0` | Incident count |
| `affectedServices` | `number` | `0` | Currently affected services |
| `totalServices` | `number` | `0` | Total monitored services |
| `timePeriod` | `string` | `'30 days'` | Time range label |
| `loading` | `boolean` | `false` | Show loading skeleton |
---
### `<upl-statuspage-assetsselector>`
Interactive service selector with filtering and search.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `services` | `IServiceStatus[]` | `[]` | Array of service objects |
| `filterText` | `string` | `''` | Search filter text |
| `filterCategory` | `string` | `'all'` | Category filter |
| `showOnlySelected` | `boolean` | `false` | Show only selected services |
| `loading` | `boolean` | `false` | Show loading state |
| `expanded` | `boolean` | `false` | Expand the selector panel |
**Events:**
- `selectionChanged` — Fired with `{ selectedServices: string[] }` when selection changes
---
### `<upl-statuspage-statusdetails>`
Hourly status timeline visualization.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `dataPoints` | `IStatusHistoryPoint[]` | `[]` | Hourly status data |
| `historyData` | `IStatusHistoryPoint[]` | `[]` | Alternative data property |
| `serviceId` | `string` | `''` | Service identifier |
| `serviceName` | `string` | `'Service'` | Display name |
| `hoursToShow` | `number` | `48` | Number of hours to display |
| `loading` | `boolean` | `false` | Show loading skeleton |
**Events:**
- `barClick` — Fired with `{ timestamp, status, responseTime, serviceId }` on bar click
---
### `<upl-statuspage-statusmonth>`
Calendar-style monthly uptime visualization.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `monthlyData` | `IMonthlyUptime[]` | `[]` | Monthly uptime data |
| `serviceId` | `string` | `'all'` | Service identifier |
| `serviceName` | `string` | `'All Services'` | Display name |
| `loading` | `boolean` | `false` | Show loading skeleton |
**Events:**
- `dayClick` — Fired with day details when a day cell is clicked
---
### `<upl-statuspage-incidents>`
Incident feed with expandable details and status updates.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `currentIncidents` | `IIncidentDetails[]` | `[]` | Active incidents |
| `pastIncidents` | `IIncidentDetails[]` | `[]` | Resolved incidents |
| `maxPastIncidents` | `number` | `10` | Max past incidents to show |
| `loading` | `boolean` | `false` | Show loading skeleton |
| `enableSubscription` | `boolean` | `false` | Allow incident subscriptions |
| `subscribedIncidentIds` | `string[]` | `[]` | Pre-subscribed incident IDs |
**Events:**
- `subscribeToIncident` — Fired with incident ID on subscription toggle
---
### `<upl-statuspage-footer>`
Full-featured footer with links, social icons, and subscription.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `companyName` | `string` | `''` | Company name |
| `legalUrl` | `string` | `''` | Terms/legal page URL |
| `supportEmail` | `string` | `''` | Support email address |
| `statusPageUrl` | `string` | `''` | Status page URL |
| `lastUpdated` | `number` | — | Last update timestamp |
| `currentYear` | `number` | `2024` | Copyright year |
| `socialLinks` | `ISocialLink[]` | `[]` | Social media links |
| `rssFeedUrl` | `string` | `''` | RSS feed URL |
| `apiStatusUrl` | `string` | `''` | Status API URL |
| `enableSubscribe` | `boolean` | `false` | Show subscribe button |
| `subscriberCount` | `number` | `0` | Display subscriber count |
| `additionalLinks` | `IFooterLink[]` | `[]` | Extra footer links |
**Events:**
- `subscribeClick` — Fired when subscribe button is clicked
---
## 📐 Interfaces
All TypeScript interfaces are exported for type safety:
```typescript
import type {
IServiceStatus,
IStatusHistoryPoint,
IIncidentDetails,
IIncidentUpdate,
IMonthlyUptime,
IUptimeDay,
IOverallStatus,
IStatusPageConfig,
ISubscription
} from '@uptime.link/statuspage';
```
### Key Interfaces
```typescript
interface IServiceStatus {
id: string;
name: string;
displayName: string;
description?: string;
currentStatus: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
lastChecked: number;
uptime30d: number;
uptime90d: number;
responseTime: number;
category?: string;
selected?: boolean;
}
interface IIncidentDetails {
id: string;
title: string;
status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
severity: 'critical' | 'major' | 'minor' | 'maintenance';
affectedServices: string[];
startTime: number;
endTime?: number;
updates: IIncidentUpdate[];
impact: string;
rootCause?: string;
resolution?: string;
}
interface IOverallStatus {
status: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
message: string;
lastUpdated: number;
affectedServices: number;
totalServices: number;
}
```
---
## 🎬 Complete Example
Build a full status page in minutes:
```typescript
import '@uptime.link/statuspage';
import type { IServiceStatus, IOverallStatus, IIncidentDetails } from '@uptime.link/statuspage';
// Get your components
const header = document.querySelector('upl-statuspage-header');
const statusBar = document.querySelector('upl-statuspage-statusbar');
const statsGrid = document.querySelector('upl-statuspage-statsgrid');
const incidents = document.querySelector('upl-statuspage-incidents');
const footer = document.querySelector('upl-statuspage-footer');
// Configure the header
header.pageTitle = 'Acme Cloud';
header.logoUrl = '/logo.svg';
header.showSubscribeButton = true;
// Set overall status
statusBar.overallStatus = {
status: 'operational',
message: 'All systems operational',
lastUpdated: Date.now(),
affectedServices: 0,
totalServices: 12
};
// Configure stats
statsGrid.uptime = 99.98;
statsGrid.avgResponseTime = 45;
statsGrid.totalServices = 12;
// Add incidents
incidents.currentIncidents = []; // No current incidents
incidents.pastIncidents = [
{ {
"compilerOptions": { id: 'inc-001',
"target": "ES2020", title: 'Scheduled Maintenance Complete',
"module": "ESNext", status: 'resolved',
"moduleResolution": "node", severity: 'maintenance',
"experimentalDecorators": true, impact: 'Database maintenance window',
"emitDecoratorMetadata": true, affectedServices: ['Database'],
"strict": true, startTime: Date.now() - 86400000,
"esModuleInterop": true, endTime: Date.now() - 82800000,
"skipLibCheck": true updates: [
}
}
```
2. **Import Components:**
Create an `index.ts` file, and import the components you need:
```typescript
import './elements/index.js';
import { page1 } from './pages/index.js';
```
3. **Creating a Status Page:**
Here, we'll create a simple status page using the imported components. We'll create individual components like headers, status bars, footers, etc., and combine them into a single page.
### UplStatuspageHeader
```typescript
import { UplStatuspageHeader } from './elements/upl-statuspage-header.js';
const header: UplStatuspageHeader = document.createElement('upl-statuspage-header');
header.pageTitle = "Uptime Link Status Page";
document.body.appendChild(header);
```
### UplStatuspageStatusbar
```typescript
import { UplStatuspageStatusbar } from './elements/upl-statuspage-statusbar.js';
const statusBar: UplStatuspageStatusbar = document.createElement('upl-statuspage-statusbar');
document.body.appendChild(statusBar);
```
### UplStatuspageAssetsselector
```typescript
import { UplStatuspageAssetsselector } from './elements/upl-statuspage-assetsselector.js';
const assetsSelector: UplStatuspageAssetsselector = document.createElement('upl-statuspage-assetsselector');
document.body.appendChild(assetsSelector);
```
### UplStatuspageStatusdetails
```typescript
import { UplStatuspageStatusdetails } from './elements/upl-statuspage-statusdetails.js';
const statusDetails: UplStatuspageStatusdetails = document.createElement('upl-statuspage-statusdetails');
document.body.appendChild(statusDetails);
```
### UplStatuspageStatusmonth
```typescript
import { UplStatuspageStatusmonth } from './elements/upl-statuspage-statusmonth.js';
const statusMonth: UplStatuspageStatusmonth = document.createElement('upl-statuspage-statusmonth');
document.body.appendChild(statusMonth);
```
### UplStatuspageIncidents
```typescript
import { UplStatuspageIncidents } from './elements/upl-statuspage-incidents.js';
import { uplInterfaces } from './plugins.js';
const incidents: UplStatuspageIncidents = document.createElement('upl-statuspage-incidents');
incidents.currentIncidences = [
// Example incident data
{ {
id: "incident1", id: 'upd-1',
title: "Server Outage", timestamp: Date.now() - 82800000,
description: "There was an outage on the main server.", status: 'resolved',
status: "resolved", message: 'Maintenance completed successfully'
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
} }
] as uplInterfaces.IIncident[]; ]
incidents.pastIncidences = [
// Example past incident data
{
id: "incident2",
title: "Database Maintenance",
description: "Scheduled maintenance on the database.",
status: "completed",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
} }
] as uplInterfaces.IIncident[]; ];
document.body.appendChild(incidents); // Configure footer
footer.companyName = 'Acme Cloud';
footer.supportEmail = 'support@acme.cloud';
footer.enableSubscribe = true;
``` ```
### UplStatuspageFooter ---
## 📄 Pre-built Page Templates
The package includes ready-to-use page templates:
```typescript ```typescript
import { UplStatuspageFooter } from './elements/upl-statuspage-footer.js'; import {
statuspageDemo, // Full demo with sample data
const footer: UplStatuspageFooter = document.createElement('upl-statuspage-footer'); statuspageAllgreen, // All systems operational
footer.legalInfo = "https://example.com/legal"; statuspageOutage, // Major outage scenario
document.body.appendChild(footer); statuspageMaintenance // Scheduled maintenance
} from '@uptime.link/statuspage/pages';
``` ```
### Full Example ---
Heres how you can put it all together to create a full status page: ## 🎨 Theming
```typescript Components automatically adapt to light/dark mode using `cssManager.bdTheme()`. The design follows a modern, minimal aesthetic with:
import './elements/index.js';
import { page1 } from './pages/index.js';
import { UplStatuspageHeader } from './elements/upl-statuspage-header.js';
import { UplStatuspageStatusbar } from './elements/upl-statuspage-statusbar.js';
import { UplStatuspageAssetsselector } from './elements/upl-statuspage-assetsselector.js';
import { UplStatuspageStatusdetails } from './elements/upl-statuspage-statusdetails.js';
import { UplStatuspageStatusmonth } from './elements/upl-statuspage-statusmonth.js';
import { UplStatuspageIncidents } from './elements/upl-statuspage-incidents.js';
import { UplStatuspageFooter } from './elements/upl-statuspage-footer.js';
import { uplInterfaces } from './plugins.js';
const header: UplStatuspageHeader = document.createElement('upl-statuspage-header'); - Clean typography with `-apple-system, BlinkMacSystemFont, 'Segoe UI'` font stack
header.pageTitle = "Uptime Link Status Page"; - Subtle shadows and borders
document.body.appendChild(header); - Semantic status colors (green, yellow, orange, red, blue)
- Smooth transitions and hover states
const statusBar: UplStatuspageStatusbar = document.createElement('upl-statuspage-statusbar'); ---
document.body.appendChild(statusBar);
const assetsSelector: UplStatuspageAssetsselector = document.createElement('upl-statuspage-assetsselector'); ## License and Legal Information
document.body.appendChild(assetsSelector);
const statusDetails: UplStatuspageStatusdetails = document.createElement('upl-statuspage-statusdetails'); This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
document.body.appendChild(statusDetails);
const statusMonth: UplStatuspageStatusmonth = document.createElement('upl-statuspage-statusmonth'); **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
document.body.appendChild(statusMonth);
const incidents: UplStatuspageIncidents = document.createElement('upl-statuspage-incidents'); ### Trademarks
incidents.currentIncidences = [
{
id: "incident1",
title: "Server Outage",
description: "There was an outage on the main server.",
status: "resolved",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
}
] as uplInterfaces.IIncident[];
incidents.pastIncidences = [ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
{
id: "incident2",
title: "Database Maintenance",
description: "Scheduled maintenance on the database.",
status: "completed",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
}
] as uplInterfaces.IIncident[];
document.body.appendChild(incidents); Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
const footer: UplStatuspageFooter = document.createElement('upl-statuspage-footer'); ### Company Information
footer.legalInfo = "https://example.com/legal";
document.body.appendChild(footer);
```
This example uses all the components provided by `@uptime.link/statuspage` to create a functional status page. Modify the data, styles, and structure to suit your requirements. Task Venture Capital GmbH
undefined Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

175
test-shadcn-spacing.html Normal file
View File

@@ -0,0 +1,175 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>shadcn-Aligned Spacing Test</title>
<script type="module" src="./dist_bundle/bundle.js"></script>
<style>
body {
margin: 0;
padding: 0;
font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif;
background: #fafafa;
}
@media (prefers-color-scheme: dark) {
body {
background: #09090b;
}
}
.demo-wrapper {
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 24px; /* Using consistent spacing between components */
}
.spacing-info {
position: fixed;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 16px;
font-size: 13px;
max-width: 300px;
z-index: 1000;
}
@media (prefers-color-scheme: dark) {
.spacing-info {
background: rgba(18, 18, 18, 0.9);
border-color: #27272a;
}
}
.spacing-info h3 {
margin: 0 0 12px 0;
font-size: 14px;
font-weight: 600;
}
.spacing-info ul {
margin: 0;
padding-left: 20px;
line-height: 1.6;
}
.spacing-info code {
background: #f3f4f6;
padding: 2px 4px;
border-radius: 3px;
font-size: 12px;
}
@media (prefers-color-scheme: dark) {
.spacing-info code {
background: #27272a;
}
}
</style>
</head>
<body>
<div class="demo-wrapper">
<upl-statuspage-header
page-title="CloudFlow"
show-report-button="true"
show-subscribe-button="true"
></upl-statuspage-header>
<upl-statuspage-pagetitle
page-title="Service Status"
page-subtitle="Current operational status of CloudFlow Infrastructure services"
></upl-statuspage-pagetitle>
<upl-statuspage-statusbar id="statusbar"></upl-statuspage-statusbar>
<upl-statuspage-statsgrid
current-status="operational"
uptime="99.95"
avg-response-time="125"
total-incidents="2"
></upl-statuspage-statsgrid>
<upl-statuspage-assetsselector id="assets"></upl-statuspage-assetsselector>
<upl-statuspage-statusdetails id="details"></upl-statuspage-statusdetails>
<upl-statuspage-statusmonth id="month"></upl-statuspage-statusmonth>
<upl-statuspage-incidents id="incidents"></upl-statuspage-incidents>
<upl-statuspage-footer
company-name="CloudFlow Infrastructure"
enable-subscribe="true"
subscriber-count="1234"
></upl-statuspage-footer>
</div>
<div class="spacing-info">
<h3>shadcn Spacing Improvements</h3>
<ul>
<li>Page title padding reduced from <code>48px</code> to <code>24px</code></li>
<li>Mini-heading padding reduced from <code>16px</code> to <code>8px</code></li>
<li>All arbitrary values replaced with spacing scale</li>
<li>Consistent <code>24px</code> gap between components</li>
<li>More compact, professional layout</li>
<li>Follows shadcn's minimal spacing approach</li>
</ul>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const statusbar = document.getElementById('statusbar');
const assets = document.getElementById('assets');
const details = document.getElementById('details');
const month = document.getElementById('month');
const incidents = document.getElementById('incidents');
// Configure status bar
statusbar.overallStatus = {
status: 'operational',
message: 'All systems operational',
lastUpdated: Date.now()
};
// Configure services
const services = [
{
id: 'api',
name: 'API Gateway',
displayName: 'API Gateway',
currentStatus: 'operational',
uptime30d: 99.99,
category: 'Core Services',
selected: true
},
{
id: 'database',
name: 'Database Cluster',
displayName: 'Database Cluster',
currentStatus: 'operational',
uptime30d: 99.95,
category: 'Core Services',
selected: true
}
];
assets.services = services;
details.services = services;
// Configure monthly data
month.monthlyData = [{
month: '2024-01',
days: Array(31).fill(null).map((_, i) => ({
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
uptime: 100,
incidents: 0
})),
overallUptime: 100,
totalIncidents: 0
}];
// Configure incidents
incidents.currentIncidents = [];
incidents.pastIncidents = [];
});
</script>
</body>
</html>

1
test/test.ts Normal file
View File

@@ -0,0 +1 @@
console.log('hello');

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@uptime.link/statuspage', name: '@uptime.link/statuspage',
version: '1.0.74', version: '1.1.0',
description: 'A catalog of web components for the UptimeLink dashboard.' description: 'A catalog of web components for the UptimeLink dashboard.'
} }

View File

@@ -1,6 +1,6 @@
import { customElement, DeesElement, html, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element'; import { customElement, DeesElement, html, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import { fonts, colors, spacing } from '../../styles/shared.styles.js'; import * as sharedStyles from '../../styles/shared.styles.js';
@customElement('uplinternal-miniheading') @customElement('uplinternal-miniheading')
export class UplinternalMiniheading extends DeesElement { export class UplinternalMiniheading extends DeesElement {
@@ -9,18 +9,18 @@ export class UplinternalMiniheading extends DeesElement {
css` css`
:host { :host {
display: block; display: block;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
h5 { h5 {
display: block; display: block;
max-width: 1200px; max-width: 1200px;
margin: 0px auto; margin: 0px auto;
padding: 0px 0px ${unsafeCSS(spacing.md)} 0px; padding: 0px 0px ${unsafeCSS(sharedStyles.spacing.sm)} 0px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
font-size: 14px; font-size: 12px;
font-weight: 600; font-weight: 600;
letter-spacing: 0.025em; letter-spacing: 0.05em;
text-transform: uppercase; text-transform: uppercase;
} }
` `

View File

@@ -10,7 +10,7 @@ import {
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import type { IServiceStatus } from '../interfaces/index.js'; import type { IServiceStatus } from '../interfaces/index.js';
import { fonts, colors, shadows, borderRadius, spacing, commonStyles, getStatusColor } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import './internal/uplinternal-miniheading.js'; import './internal/uplinternal-miniheading.js';
import { demoFunc } from './upl-statuspage-assetsselector.demo.js'; import { demoFunc } from './upl-statuspage-assetsselector.demo.js';
@@ -26,22 +26,22 @@ export class UplStatuspageAssetsselector extends DeesElement {
public static demo = demoFunc; public static demo = demoFunc;
@property({ type: Array }) @property({ type: Array })
public services: IServiceStatus[] = []; accessor services: IServiceStatus[] = [];
@property({ type: String }) @property({ type: String })
public filterText: string = ''; accessor filterText: string = '';
@property({ type: String }) @property({ type: String })
public filterCategory: string = 'all'; accessor filterCategory: string = 'all';
@property({ type: Boolean }) @property({ type: Boolean })
public showOnlySelected: boolean = false; accessor showOnlySelected: boolean = false;
@property({ type: Boolean }) @property({ type: Boolean })
public loading: boolean = false; accessor loading: boolean = false;
@property({ type: Boolean }) @property({ type: Boolean })
public expanded: boolean = false; accessor expanded: boolean = false;
constructor() { constructor() {
super(); super();
@@ -49,25 +49,25 @@ export class UplStatuspageAssetsselector extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
commonStyles, sharedStyles.commonStyles,
css` css`
:host { :host {
display: block; display: block;
background: transparent; background: transparent;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.container { .container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.controls { .controls {
display: flex; display: flex;
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
margin-bottom: ${unsafeCSS(spacing.md)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
} }
@@ -75,13 +75,13 @@ export class UplStatuspageAssetsselector extends DeesElement {
.search-input { .search-input {
flex: 1; flex: 1;
min-width: 200px; min-width: 200px;
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
font-size: 13px; font-size: 13px;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
transition: all 0.2s ease; transition: all 0.2s ease;
height: 32px; height: 32px;
} }
@@ -99,15 +99,15 @@ export class UplStatuspageAssetsselector extends DeesElement {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
background: transparent; background: transparent;
color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
cursor: pointer; cursor: pointer;
font-size: 13px; font-size: 13px;
font-weight: 400; font-weight: 400;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
transition: all 0.2s ease; transition: all 0.2s ease;
height: 32px; height: 32px;
} }
@@ -126,18 +126,18 @@ export class UplStatuspageAssetsselector extends DeesElement {
.selected-services { .selected-services {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
align-items: center; align-items: center;
} }
.service-pill { .service-pill {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.md)};
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.full)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
font-size: 13px; font-size: 13px;
transition: all 0.2s ease; transition: all 0.2s ease;
} }
@@ -155,17 +155,17 @@ export class UplStatuspageAssetsselector extends DeesElement {
.manage-button { .manage-button {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.md)};
background: transparent; background: transparent;
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
.manage-button:hover { .manage-button:hover {
@@ -174,35 +174,35 @@ export class UplStatuspageAssetsselector extends DeesElement {
} }
.expandable-section { .expandable-section {
margin-top: ${unsafeCSS(spacing.lg)}; margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
overflow: hidden; overflow: hidden;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.expandable-content { .expandable-content {
padding: ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(sharedStyles.spacing.lg)};
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
} }
.assets-grid { .assets-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
margin-top: ${unsafeCSS(spacing.md)}; margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.asset-card { .asset-card {
display: flex; display: flex;
align-items: center; align-items: center;
padding: ${unsafeCSS(spacing.sm)} ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.sm)} ${unsafeCSS(sharedStyles.spacing.md)};
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
} }
.asset-card:hover { .asset-card:hover {
@@ -218,7 +218,7 @@ export class UplStatuspageAssetsselector extends DeesElement {
width: 16px; width: 16px;
height: 16px; height: 16px;
cursor: pointer; cursor: pointer;
accent-color: ${colors.text.primary}; accent-color: ${sharedStyles.colors.text.primary};
flex-shrink: 0; flex-shrink: 0;
} }
@@ -230,7 +230,7 @@ export class UplStatuspageAssetsselector extends DeesElement {
.asset-name { .asset-name {
font-weight: 600; font-weight: 600;
font-size: 14px; font-size: 14px;
margin-bottom: ${unsafeCSS(spacing.xs)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.xs)};
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -238,7 +238,7 @@ export class UplStatuspageAssetsselector extends DeesElement {
.asset-description { .asset-description {
font-size: 13px; font-size: 13px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -247,14 +247,14 @@ export class UplStatuspageAssetsselector extends DeesElement {
.asset-status { .asset-status {
display: flex; display: flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
flex-shrink: 0; flex-shrink: 0;
} }
.status-indicator { .status-indicator {
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: ${unsafeCSS(borderRadius.full)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
} }
.status-indicator.operational, .status-dot.operational { background: #22c55e; } .status-indicator.operational, .status-dot.operational { background: #22c55e; }
@@ -266,14 +266,14 @@ export class UplStatuspageAssetsselector extends DeesElement {
.status-text { .status-text {
font-size: 12px; font-size: 12px;
text-transform: capitalize; text-transform: capitalize;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
} }
.loading-message, .loading-message,
.no-results { .no-results {
grid-column: 1 / -1; grid-column: 1 / -1;
text-align: center; text-align: center;
padding: ${unsafeCSS(spacing.xl)}; padding: ${unsafeCSS(sharedStyles.spacing.xl)};
color: ${cssManager.bdTheme('#9ca3af', '#71717a')}; color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
font-size: 13px; font-size: 13px;
} }
@@ -281,12 +281,12 @@ export class UplStatuspageAssetsselector extends DeesElement {
.summary { .summary {
text-align: right; text-align: right;
font-size: 12px; font-size: 12px;
margin-top: ${unsafeCSS(spacing.md)}; margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
color: ${cssManager.bdTheme('#9ca3af', '#71717a')}; color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
} }
.no-services { .no-services {
padding: ${unsafeCSS(spacing.xl)}; padding: ${unsafeCSS(sharedStyles.spacing.xl)};
text-align: center; text-align: center;
color: ${cssManager.bdTheme('#9ca3af', '#71717a')}; color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
font-size: 13px; font-size: 13px;
@@ -294,7 +294,7 @@ export class UplStatuspageAssetsselector extends DeesElement {
@media (max-width: 640px) { @media (max-width: 640px) {
.container { .container {
padding: 0 ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
} }
.controls { .controls {
@@ -316,7 +316,7 @@ export class UplStatuspageAssetsselector extends DeesElement {
} }
.expandable-content { .expandable-content {
padding: ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.assets-grid { .assets-grid {
@@ -324,7 +324,7 @@ export class UplStatuspageAssetsselector extends DeesElement {
} }
.asset-card { .asset-card {
padding: ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.sm)};
} }
} }
`, `,

View File

@@ -1,6 +1,6 @@
import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element'; import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import { fonts, colors, shadows, borderRadius, spacing, commonStyles } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import { demoFunc } from './upl-statuspage-footer.demo.js'; import { demoFunc } from './upl-statuspage-footer.demo.js';
declare global { declare global {
@@ -16,76 +16,76 @@ export class UplStatuspageFooter extends DeesElement {
// INSTANCE // INSTANCE
@property({ type: String }) @property({ type: String })
public companyName: string = ''; accessor companyName: string = '';
@property({ type: String }) @property({ type: String })
public legalUrl: string = ''; accessor legalUrl: string = '';
@property({ type: String }) @property({ type: String })
public supportEmail: string = ''; accessor supportEmail: string = '';
@property({ type: String }) @property({ type: String })
public statusPageUrl: string = ''; accessor statusPageUrl: string = '';
@property({ type: Boolean }) @property({ type: Boolean })
public whitelabel: boolean = false; accessor whitelabel: boolean = false;
@property({ type: Number }) @property({ type: Number })
public lastUpdated: number | null = null; accessor lastUpdated: number | null = null;
@property({ type: Number }) @property({ type: Number })
public currentYear: number = new Date().getFullYear(); accessor currentYear: number = new Date().getFullYear();
@property({ type: Array }) @property({ type: Array })
public socialLinks: Array<{ platform: string; url: string }> = []; accessor socialLinks: Array<{ platform: string; url: string }> = [];
@property({ type: Array }) @property({ type: Array })
public additionalLinks: Array<{ label: string; url: string }> = []; accessor additionalLinks: Array<{ label: string; url: string }> = [];
@property({ type: String }) @property({ type: String })
public rssFeedUrl: string = ''; accessor rssFeedUrl: string = '';
@property({ type: String }) @property({ type: String })
public apiStatusUrl: string = ''; accessor apiStatusUrl: string = '';
@property({ type: Boolean }) @property({ type: Boolean })
public loading: boolean = false; accessor loading: boolean = false;
@property({ type: String }) @property({ type: String })
public errorMessage: string | null = null; accessor errorMessage: string | null = null;
@property({ type: Boolean }) @property({ type: Boolean })
public offline: boolean = false; accessor offline: boolean = false;
@property({ type: String }) @property({ type: String })
public latestStatusUpdate: string = ''; accessor latestStatusUpdate: string = '';
@property({ type: Boolean }) @property({ type: Boolean })
public enableSubscribe: boolean = false; accessor enableSubscribe: boolean = false;
@property({ type: Boolean }) @property({ type: Boolean })
public enableReportIssue: boolean = false; accessor enableReportIssue: boolean = false;
@property({ type: Boolean }) @property({ type: Boolean })
public enableLanguageSelector: boolean = false; accessor enableLanguageSelector: boolean = false;
@property({ type: Boolean }) @property({ type: Boolean })
public enableThemeToggle: boolean = false; accessor enableThemeToggle: boolean = false;
@property({ type: Array }) @property({ type: Array })
public languageOptions: Array<{ code: string; label: string }> = []; accessor languageOptions: Array<{ code: string; label: string }> = [];
@property({ type: String }) @property({ type: String })
public currentLanguage: string = 'en'; accessor currentLanguage: string = 'en';
@property({ type: String }) @property({ type: String })
public currentTheme: string = 'light'; accessor currentTheme: string = 'light';
@property({ type: Number }) @property({ type: Number })
public subscriberCount: number = 0; accessor subscriberCount: number = 0;
@property({ type: Object }) @property({ type: Object })
public customBranding: { primaryColor?: string; logoUrl?: string; footerText?: string } | null = null; accessor customBranding: { primaryColor?: string; logoUrl?: string; footerText?: string } | null = null;
constructor() { constructor() {
@@ -94,53 +94,53 @@ export class UplStatuspageFooter extends DeesElement {
public static styles = [ public static styles = [
domtools.elementBasic.staticStyles, domtools.elementBasic.staticStyles,
commonStyles, sharedStyles.commonStyles,
css` css`
:host { :host {
display: block; display: block;
background: ${colors.background.primary}; background: ${sharedStyles.colors.background.primary};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
font-size: 14px; font-size: 14px;
border-top: 1px solid ${colors.border.default}; border-top: 1px solid ${sharedStyles.colors.border.default};
} }
.container { .container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: ${unsafeCSS(spacing['2xl'])} ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(sharedStyles.spacing['2xl'])} ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.footer-content { .footer-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.xl)}; gap: ${unsafeCSS(sharedStyles.spacing.xl)};
} }
.footer-main { .footer-main {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: start; align-items: start;
gap: ${unsafeCSS(spacing['2xl'])}; gap: ${unsafeCSS(sharedStyles.spacing['2xl'])};
} }
.company-info { .company-info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.lg)}; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.company-name { .company-name {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
letter-spacing: -0.01em; letter-spacing: -0.01em;
} }
.company-links { .company-links {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: ${unsafeCSS(spacing.lg)}; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.footer-link { .footer-link {
@@ -152,13 +152,13 @@ export class UplStatuspageFooter extends DeesElement {
} }
.footer-link:hover { .footer-link:hover {
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.footer-actions { .footer-actions {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.action-button { .action-button {
@@ -173,8 +173,8 @@ export class UplStatuspageFooter extends DeesElement {
white-space: nowrap; white-space: nowrap;
font-size: 13px; font-size: 13px;
font-weight: 400; font-weight: 400;
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -189,21 +189,21 @@ export class UplStatuspageFooter extends DeesElement {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding-top: ${unsafeCSS(spacing.lg)}; padding-top: ${unsafeCSS(sharedStyles.spacing.lg)};
margin-top: ${unsafeCSS(spacing.lg)}; margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
border-top: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border-top: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
} }
.footer-meta { .footer-meta {
display: flex; display: flex;
gap: ${unsafeCSS(spacing.lg)}; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
} }
.social-links { .social-links {
display: flex; display: flex;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
align-items: center; align-items: center;
} }
@@ -219,7 +219,7 @@ export class UplStatuspageFooter extends DeesElement {
} }
.social-link:hover { .social-link:hover {
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
background: ${cssManager.bdTheme('#f3f4f6', '#27272a')}; background: ${cssManager.bdTheme('#f3f4f6', '#27272a')};
} }
@@ -252,7 +252,7 @@ export class UplStatuspageFooter extends DeesElement {
} }
.powered-by a:hover { .powered-by a:hover {
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.status-update { .status-update {
@@ -261,7 +261,7 @@ export class UplStatuspageFooter extends DeesElement {
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: 6px; border-radius: 6px;
font-size: 13px; font-size: 13px;
margin-bottom: ${unsafeCSS(spacing.lg)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
line-height: 1.5; line-height: 1.5;
color: ${cssManager.bdTheme('#4b5563', '#d1d5db')}; color: ${cssManager.bdTheme('#4b5563', '#d1d5db')};
} }
@@ -271,38 +271,38 @@ export class UplStatuspageFooter extends DeesElement {
} }
.language-selector select { .language-selector select {
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
border: 1px solid ${colors.border.default}; border: 1px solid ${sharedStyles.colors.border.default};
background: ${colors.background.primary}; background: ${sharedStyles.colors.background.primary};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
.theme-toggle { .theme-toggle {
cursor: pointer; cursor: pointer;
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
border: 1px solid ${colors.border.default}; border: 1px solid ${sharedStyles.colors.border.default};
background: transparent; background: transparent;
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
font-size: 14px; font-size: 14px;
transition: all 0.2s ease; transition: all 0.2s ease;
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
.theme-toggle:hover { .theme-toggle:hover {
background: ${colors.background.secondary}; background: ${sharedStyles.colors.background.secondary};
border-color: ${colors.border.muted}; border-color: ${sharedStyles.colors.border.muted};
} }
.subscribe-wrapper { .subscribe-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
} }
.subscriber-count { .subscriber-count {
@@ -311,11 +311,11 @@ export class UplStatuspageFooter extends DeesElement {
} }
.error-message { .error-message {
padding: ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.md)};
background: ${cssManager.bdTheme('#fee9e9', '#7f1d1d')}; background: ${cssManager.bdTheme('#fee9e9', '#7f1d1d')};
color: ${cssManager.bdTheme('#dc2626', '#fca5a5')}; color: ${cssManager.bdTheme('#dc2626', '#fca5a5')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
margin-bottom: ${unsafeCSS(spacing.lg)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
font-size: 14px; font-size: 14px;
border: 1px solid ${cssManager.bdTheme('#fecaca', '#991b1b')}; border: 1px solid ${cssManager.bdTheme('#fecaca', '#991b1b')};
} }
@@ -323,11 +323,11 @@ export class UplStatuspageFooter extends DeesElement {
.offline-indicator { .offline-indicator {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.md)};
background: ${colors.status.degraded}; background: ${sharedStyles.colors.status.degraded};
color: white; color: white;
border-radius: ${unsafeCSS(borderRadius.full)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
} }
@@ -340,7 +340,7 @@ export class UplStatuspageFooter extends DeesElement {
)}; )};
background-size: 200% 100%; background-size: 200% 100%;
animation: loading 1.5s infinite; animation: loading 1.5s infinite;
border-radius: ${unsafeCSS(borderRadius.md)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.md)};
} }
@keyframes loading { @keyframes loading {
@@ -351,8 +351,8 @@ export class UplStatuspageFooter extends DeesElement {
.additional-links { .additional-links {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
margin-top: ${unsafeCSS(spacing.md)}; margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.additional-link { .additional-link {
@@ -363,34 +363,34 @@ export class UplStatuspageFooter extends DeesElement {
} }
.additional-link:hover { .additional-link:hover {
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.container { .container {
padding: ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.md)};
} }
.footer-main { .footer-main {
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.lg)}; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.footer-bottom { .footer-bottom {
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.lg)}; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
align-items: start; align-items: start;
} }
.footer-meta { .footer-meta {
flex-direction: column; flex-direction: column;
align-items: start; align-items: start;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.company-links { .company-links {
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
} }
.powered-by { .powered-by {

View File

@@ -1,6 +1,6 @@
import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element'; import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import { fonts, shadows } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import { demoFunc } from './upl-statuspage-header.demo.js'; import { demoFunc } from './upl-statuspage-header.demo.js';
declare global { declare global {
@@ -16,19 +16,19 @@ export class UplStatuspageHeader extends DeesElement {
// INSTANCE // INSTANCE
@property({ type: String }) @property({ type: String })
public pageTitle: string = "Statuspage Title"; accessor pageTitle: string = "Statuspage Title";
@property({ type: Boolean }) @property({ type: Boolean })
public showReportButton: boolean = true; accessor showReportButton: boolean = true;
@property({ type: Boolean }) @property({ type: Boolean })
public showSubscribeButton: boolean = true; accessor showSubscribeButton: boolean = true;
@property({ type: String }) @property({ type: String })
public logoUrl: string = ''; accessor logoUrl: string = '';
@property({ type: Boolean }) @property({ type: Boolean })
public loading: boolean = false; accessor loading: boolean = false;
constructor() { constructor() {
@@ -40,19 +40,21 @@ export class UplStatuspageHeader extends DeesElement {
css` css`
:host { :host {
display: block; display: block;
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${sharedStyles.colors.background.primary};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${sharedStyles.colors.text.primary};
border-bottom: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border-bottom: 1px solid ${sharedStyles.colors.border.default};
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 40; z-index: 40;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
} }
.header-container { .header-container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 24px; padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.header-nav { .header-nav {
@@ -65,108 +67,163 @@ export class UplStatuspageHeader extends DeesElement {
.header-left { .header-left {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 24px; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.header-actions { .header-actions {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
} }
.actionButton { .actionButton {
font-size: 14px; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
font-size: 13px;
font-weight: 500; font-weight: 500;
padding: 8px 12px; padding: 0 14px;
border-radius: 6px; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
transition: all 0.2s ease; transition: all ${unsafeCSS(sharedStyles.durations.normal)} ${unsafeCSS(sharedStyles.easings.default)};
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 36px; height: 36px;
background: transparent; background: transparent;
border: none; border: 1px solid ${sharedStyles.colors.border.default};
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${sharedStyles.colors.text.primary};
letter-spacing: -0.01em;
} }
.actionButton:hover { .actionButton:hover {
background: ${cssManager.bdTheme('#f4f4f5', '#27272a')}; background: ${sharedStyles.colors.background.secondary};
border-color: ${sharedStyles.colors.border.muted};
box-shadow: ${unsafeCSS(sharedStyles.shadows.xs)};
}
.actionButton:active {
transform: scale(0.98);
transition-duration: ${unsafeCSS(sharedStyles.durations.fast)};
}
.actionButton:focus-visible {
outline: 2px solid ${sharedStyles.colors.accent.focus};
outline-offset: 2px;
} }
.site-title { .site-title {
font-size: 20px; font-size: 18px;
font-weight: 600; font-weight: 600;
letter-spacing: -0.02em; letter-spacing: -0.02em;
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${sharedStyles.colors.text.primary};
} }
.logo { .logo {
height: 24px; height: 28px;
width: auto; width: auto;
filter: ${cssManager.bdTheme('none', 'brightness(0) invert(1)')}; filter: ${cssManager.bdTheme('none', 'brightness(0) invert(1)')};
transition: opacity ${unsafeCSS(sharedStyles.durations.normal)} ${unsafeCSS(sharedStyles.easings.default)};
}
.logo:hover {
opacity: 0.8;
} }
.page-info { .page-info {
padding: 48px 0 64px 0; padding: ${unsafeCSS(sharedStyles.spacing.lg)} 0 ${unsafeCSS(sharedStyles.spacing.xl)} 0;
} }
.page-title { .page-title {
font-size: 48px; font-size: 48px;
font-weight: 700; font-weight: 700;
letter-spacing: -0.02em; letter-spacing: -0.03em;
line-height: 1.1; line-height: 1.1;
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${sharedStyles.colors.text.primary};
margin: 0 0 16px 0; margin: 0 0 16px 0;
} }
.page-subtitle { .page-subtitle {
font-size: 20px; font-size: 18px;
color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; color: ${sharedStyles.colors.text.secondary};
margin: 0; margin: 0;
line-height: 1.5; line-height: 1.5;
} }
/* Primary button variant */ /* Primary button variant */
.actionButton.primary { .actionButton.primary {
background: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; background: ${sharedStyles.colors.accent.primary};
color: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; color: ${sharedStyles.colors.background.primary};
border: 1px solid ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; border-color: transparent;
} }
.actionButton.primary:hover { .actionButton.primary:hover {
background: ${cssManager.bdTheme('#262626', '#e5e7eb')}; background: ${sharedStyles.colors.accent.hover};
border-color: ${cssManager.bdTheme('#262626', '#e5e7eb')}; box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
} }
.loading-skeleton { .loading-skeleton {
height: 64px; height: 64px;
background: ${cssManager.bdTheme('#f9fafb', '#0a0a0a')}; background: ${sharedStyles.colors.background.secondary};
border-bottom: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border-bottom: 1px solid ${sharedStyles.colors.border.default};
position: relative;
overflow: hidden;
}
.loading-skeleton::after {
content: '';
position: absolute;
inset: 0;
background: ${cssManager.bdTheme(
'linear-gradient(90deg, transparent 0%, rgba(0,0,0,0.04) 50%, transparent 100%)',
'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.04) 50%, transparent 100%)'
)};
animation: loading 1.5s ${unsafeCSS(sharedStyles.easings.default)} infinite;
}
@keyframes loading {
0% { transform: translateX(-100%); }
100% { transform: translateX(200%); }
}
@media (max-width: 768px) {
.header-container {
padding: 0 ${unsafeCSS(sharedStyles.spacing.md)};
} }
@media (max-width: 640px) {
.header-nav { .header-nav {
height: 56px; height: 56px;
} }
.site-title { .header-left {
font-size: 18px; gap: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.site-title {
font-size: 16px;
}
.logo {
height: 24px;
}
}
@media (max-width: 640px) {
.actionButton { .actionButton {
font-size: 13px; font-size: 12px;
padding: 6px 10px; padding: 0 12px;
height: 32px; height: 32px;
} }
.page-title { .page-title {
font-size: 36px; font-size: 32px;
} }
.page-subtitle { .page-subtitle {
font-size: 18px; font-size: 16px;
}
.header-actions {
gap: 6px;
} }
} }
` `

View File

@@ -10,7 +10,7 @@ import {
unsafeCSS, unsafeCSS,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import type { IIncidentDetails } from '../interfaces/index.js'; import type { IIncidentDetails } from '../interfaces/index.js';
import { fonts, colors, shadows, borderRadius, spacing, commonStyles } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import './internal/uplinternal-miniheading.js'; import './internal/uplinternal-miniheading.js';
import { demoFunc } from './upl-statuspage-incidents.demo.js'; import { demoFunc } from './upl-statuspage-incidents.demo.js';
@@ -29,44 +29,44 @@ export class UplStatuspageIncidents extends DeesElement {
@property({ @property({
type: Array, type: Array,
}) })
public currentIncidents: IIncidentDetails[] = []; accessor currentIncidents: IIncidentDetails[] = [];
@property({ @property({
type: Array, type: Array,
}) })
public pastIncidents: IIncidentDetails[] = []; accessor pastIncidents: IIncidentDetails[] = [];
@property({ @property({
type: Boolean, type: Boolean,
}) })
public whitelabel = false; accessor whitelabel = false;
@property({ @property({
type: Boolean, type: Boolean,
}) })
public loading = false; accessor loading = false;
@property({ @property({
type: Number, type: Number,
}) })
public daysToShow = 90; accessor daysToShow = 90;
@property({ @property({
type: Array, type: Array,
}) })
public subscribedIncidentIds: string[] = []; accessor subscribedIncidentIds: string[] = [];
@property({ @property({
type: Object, type: Object,
state: true, state: true,
}) })
private expandedIncidents: Set<string> = new Set(); private accessor expandedIncidents: Set<string> = new Set();
@property({ @property({
type: Object, type: Object,
state: true, state: true,
}) })
private subscribedIncidents: Set<string> = new Set(); private accessor subscribedIncidents: Set<string> = new Set();
constructor() { constructor() {
super(); super();
@@ -89,53 +89,53 @@ export class UplStatuspageIncidents extends DeesElement {
public static styles = [ public static styles = [
plugins.domtools.elementBasic.staticStyles, plugins.domtools.elementBasic.staticStyles,
commonStyles, sharedStyles.commonStyles,
css` css`
:host { :host {
display: block; display: block;
background: transparent; background: transparent;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.container { .container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.noIncidentBox { .noIncidentBox {
background: ${colors.background.card}; background: ${sharedStyles.colors.background.card};
padding: ${unsafeCSS(spacing.xl)}; padding: ${unsafeCSS(sharedStyles.spacing.xl)};
margin-bottom: ${unsafeCSS(spacing.lg)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
border-radius: ${unsafeCSS(borderRadius.md)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.md)};
border: 1px solid ${colors.border.default}; border: 1px solid ${sharedStyles.colors.border.default};
text-align: center; text-align: center;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
box-shadow: ${unsafeCSS(shadows.sm)}; box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
} }
.incident-card { .incident-card {
background: ${colors.background.card}; background: ${sharedStyles.colors.background.card};
border-radius: ${unsafeCSS(borderRadius.md)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.md)};
margin-bottom: ${unsafeCSS(spacing.lg)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
overflow: hidden; overflow: hidden;
box-shadow: ${unsafeCSS(shadows.sm)}; box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
border: 1px solid ${colors.border.default}; border: 1px solid ${sharedStyles.colors.border.default};
transition: all 0.2s ease; transition: all 0.2s ease;
} }
.incident-card.expanded { .incident-card.expanded {
box-shadow: ${unsafeCSS(shadows.md)}; box-shadow: ${unsafeCSS(sharedStyles.shadows.md)};
} }
.incident-header { .incident-header {
padding: ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
border-left: 4px solid; border-left: 4px solid;
display: flex; display: flex;
align-items: start; align-items: start;
justify-content: space-between; justify-content: space-between;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
cursor: pointer; cursor: pointer;
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
} }
@@ -145,19 +145,19 @@ export class UplStatuspageIncidents extends DeesElement {
} }
.incident-header.critical { .incident-header.critical {
border-left-color: ${colors.status.major}; border-left-color: ${sharedStyles.colors.status.major};
} }
.incident-header.major { .incident-header.major {
border-left-color: ${colors.status.partial}; border-left-color: ${sharedStyles.colors.status.partial};
} }
.incident-header.minor { .incident-header.minor {
border-left-color: ${colors.status.degraded}; border-left-color: ${sharedStyles.colors.status.degraded};
} }
.incident-header.maintenance { .incident-header.maintenance {
border-left-color: ${colors.status.maintenance}; border-left-color: ${sharedStyles.colors.status.maintenance};
} }
.incident-title { .incident-title {
@@ -169,19 +169,19 @@ export class UplStatuspageIncidents extends DeesElement {
.incident-meta { .incident-meta {
display: flex; display: flex;
gap: ${unsafeCSS(spacing.lg)}; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
margin-top: ${unsafeCSS(spacing.sm)}; margin-top: ${unsafeCSS(sharedStyles.spacing.sm)};
font-size: 13px; font-size: 13px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
flex-wrap: wrap; flex-wrap: wrap;
} }
.incident-status { .incident-status {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.md)};
border-radius: ${unsafeCSS(borderRadius.full)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
@@ -215,49 +215,49 @@ export class UplStatuspageIncidents extends DeesElement {
} }
.incident-body { .incident-body {
padding: 0 ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.incident-impact { .incident-impact {
margin: ${unsafeCSS(spacing.md)} 0; margin: ${unsafeCSS(sharedStyles.spacing.md)} 0;
padding: ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.md)};
background: ${colors.background.secondary}; background: ${sharedStyles.colors.background.secondary};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
font-size: 14px; font-size: 14px;
line-height: 1.6; line-height: 1.6;
} }
.affected-services { .affected-services {
margin-top: ${unsafeCSS(spacing.md)}; margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.affected-services-title { .affected-services-title {
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
margin-bottom: ${unsafeCSS(spacing.sm)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.service-tag { .service-tag {
display: inline-block; display: inline-block;
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
margin: 2px; margin: 2px;
background: ${colors.background.muted}; background: ${sharedStyles.colors.background.muted};
border-radius: ${unsafeCSS(borderRadius.sm)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.sm)};
font-size: 12px; font-size: 12px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
} }
.incident-updates { .incident-updates {
margin-top: ${unsafeCSS(spacing.lg)}; margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
border-top: 1px solid ${colors.border.default}; border-top: 1px solid ${sharedStyles.colors.border.default};
padding-top: ${unsafeCSS(spacing.lg)}; padding-top: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.update-item { .update-item {
position: relative; position: relative;
padding-left: ${unsafeCSS(spacing.lg)}; padding-left: ${unsafeCSS(sharedStyles.spacing.lg)};
margin-bottom: ${unsafeCSS(spacing.md)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.update-item::before { .update-item::before {
@@ -267,27 +267,27 @@ export class UplStatuspageIncidents extends DeesElement {
top: 6px; top: 6px;
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: ${unsafeCSS(borderRadius.full)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
background: ${colors.border.muted}; background: ${sharedStyles.colors.border.muted};
} }
.update-time { .update-time {
font-size: 12px; font-size: 12px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
margin-bottom: ${unsafeCSS(spacing.xs)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.xs)};
font-family: ${unsafeCSS(fonts.mono)}; font-family: ${unsafeCSS(sharedStyles.fonts.mono)};
} }
.update-message { .update-message {
font-size: 14px; font-size: 14px;
line-height: 1.6; line-height: 1.6;
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.update-author { .update-author {
font-size: 12px; font-size: 12px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
margin-top: ${unsafeCSS(spacing.xs)}; margin-top: ${unsafeCSS(sharedStyles.spacing.xs)};
font-style: italic; font-style: italic;
} }
@@ -299,8 +299,8 @@ export class UplStatuspageIncidents extends DeesElement {
)}; )};
background-size: 200% 100%; background-size: 200% 100%;
animation: loading 1.5s infinite; animation: loading 1.5s infinite;
border-radius: ${unsafeCSS(borderRadius.md)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.md)};
margin-bottom: ${unsafeCSS(spacing.lg)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
@keyframes loading { @keyframes loading {
@@ -310,28 +310,28 @@ export class UplStatuspageIncidents extends DeesElement {
.show-more { .show-more {
text-align: center; text-align: center;
margin-top: ${unsafeCSS(spacing.lg)}; margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.show-more-button { .show-more-button {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: ${unsafeCSS(spacing.sm)} ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(sharedStyles.spacing.sm)} ${unsafeCSS(sharedStyles.spacing.lg)};
background: transparent; background: transparent;
border: 1px solid ${colors.border.default}; border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
transition: all 0.2s ease; transition: all 0.2s ease;
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
.show-more-button:hover { .show-more-button:hover {
background: ${colors.background.secondary}; background: ${sharedStyles.colors.background.secondary};
border-color: ${colors.border.muted}; border-color: ${sharedStyles.colors.border.muted};
transform: translateY(-1px); transform: translateY(-1px);
} }
@@ -341,32 +341,32 @@ export class UplStatuspageIncidents extends DeesElement {
.incident-actions { .incident-actions {
display: flex; display: flex;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
align-items: center; align-items: center;
margin-top: ${unsafeCSS(spacing.lg)}; margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
padding-top: ${unsafeCSS(spacing.lg)}; padding-top: ${unsafeCSS(sharedStyles.spacing.lg)};
border-top: 1px solid ${colors.border.default}; border-top: 1px solid ${sharedStyles.colors.border.default};
} }
.subscribe-button { .subscribe-button {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.md)};
background: transparent; background: transparent;
border: 1px solid ${colors.border.default}; border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer; cursor: pointer;
font-size: 13px; font-size: 13px;
font-weight: 400; font-weight: 400;
transition: all 0.2s ease; transition: all 0.2s ease;
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
.subscribe-button:hover { .subscribe-button:hover {
background: ${colors.background.secondary}; background: ${sharedStyles.colors.background.secondary};
border-color: ${colors.border.muted}; border-color: ${sharedStyles.colors.border.muted};
} }
.subscribe-button.subscribed { .subscribe-button.subscribed {
@@ -381,24 +381,24 @@ export class UplStatuspageIncidents extends DeesElement {
.collapsed-hint { .collapsed-hint {
font-size: 12px; font-size: 12px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
text-align: center; text-align: center;
margin-top: ${unsafeCSS(spacing.md)}; margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
opacity: 0.8; opacity: 0.8;
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.container { .container {
padding: 0 ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
} }
.incident-header { .incident-header {
padding: ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.incident-meta { .incident-meta {
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
} }
} }
`, `,
@@ -456,12 +456,12 @@ export class UplStatuspageIncidents extends DeesElement {
</div> </div>
${!this.expandedIncidents.has(incident.id) ? html` ${!this.expandedIncidents.has(incident.id) ? html`
<div style=" <div style="
margin-top: ${unsafeCSS(spacing.sm)}; margin-top: ${unsafeCSS(sharedStyles.spacing.sm)};
font-size: 13px; font-size: 13px;
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
display: flex; display: flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
"> ">
${incident.impact ? html` ${incident.impact ? html`
<span style=" <span style="
@@ -480,7 +480,7 @@ export class UplStatuspageIncidents extends DeesElement {
</div> </div>
` : ''} ` : ''}
</div> </div>
<div style="display: flex; align-items: center; gap: ${unsafeCSS(spacing.md)};"> <div style="display: flex; align-items: center; gap: ${unsafeCSS(sharedStyles.spacing.md)};">
<div class="incident-status ${latestUpdate.status}"> <div class="incident-status ${latestUpdate.status}">
${this.getStatusIcon(latestUpdate.status)} ${this.getStatusIcon(latestUpdate.status)}
${latestUpdate.status.replace(/_/g, ' ')} ${latestUpdate.status.replace(/_/g, ' ')}
@@ -590,10 +590,10 @@ export class UplStatuspageIncidents extends DeesElement {
height: 6px; height: 6px;
border-radius: 50%; border-radius: 50%;
margin-right: 4px; margin-right: 4px;
background: ${status === 'resolved' ? colors.status.operational : background: ${status === 'resolved' ? sharedStyles.colors.status.operational :
status === 'monitoring' ? colors.status.maintenance : status === 'monitoring' ? sharedStyles.colors.status.maintenance :
status === 'identified' ? colors.status.degraded : status === 'identified' ? sharedStyles.colors.status.degraded :
colors.status.partial}; sharedStyles.colors.status.partial};
"></span>`; "></span>`;
} }

View File

@@ -1,6 +1,6 @@
import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element'; import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import { fonts } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import { demoFunc } from './upl-statuspage-pagetitle.demo.js'; import { demoFunc } from './upl-statuspage-pagetitle.demo.js';
declare global { declare global {
@@ -14,13 +14,13 @@ export class UplStatuspagePagetitle extends DeesElement {
public static demo = demoFunc; public static demo = demoFunc;
@property({ type: String }) @property({ type: String })
public pageTitle: string = 'System Status'; accessor pageTitle: string = 'System Status';
@property({ type: String }) @property({ type: String })
public pageSubtitle: string = ''; accessor pageSubtitle: string = '';
@property({ type: Boolean }) @property({ type: Boolean })
public centered: boolean = false; accessor centered: boolean = false;
constructor() { constructor() {
super(); super();
@@ -31,13 +31,13 @@ export class UplStatuspagePagetitle extends DeesElement {
css` css`
:host { :host {
display: block; display: block;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
.title-container { .title-container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 48px 24px 24px 24px; padding: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.title-container.centered { .title-container.centered {
@@ -47,30 +47,30 @@ export class UplStatuspagePagetitle extends DeesElement {
h1 { h1 {
font-size: 48px; font-size: 48px;
font-weight: 700; font-weight: 700;
letter-spacing: -0.02em; letter-spacing: -0.03em;
line-height: 1.1; line-height: 1.1;
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${sharedStyles.colors.text.primary};
margin: 0 0 16px 0; margin: 0 0 ${unsafeCSS(sharedStyles.spacing.md)} 0;
}
p {
font-size: 20px;
color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
margin: 0;
line-height: 1.5;
}
@media (max-width: 640px) {
.title-container {
padding: 32px 16px 20px 16px;
}
h1 {
font-size: 36px;
} }
p { p {
font-size: 18px; font-size: 18px;
color: ${sharedStyles.colors.text.secondary};
margin: 0;
line-height: 1.6;
}
@media (max-width: 640px) {
.title-container {
padding: ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.md)};
}
h1 {
font-size: 32px;
}
p {
font-size: 16px;
} }
} }
` `

View File

@@ -9,7 +9,7 @@ import {
unsafeCSS, unsafeCSS,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import { fonts, colors, shadows, borderRadius, spacing, commonStyles } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import './internal/uplinternal-miniheading.js'; import './internal/uplinternal-miniheading.js';
import { demoFunc } from './upl-statuspage-statsgrid.demo.js'; import { demoFunc } from './upl-statuspage-statsgrid.demo.js';
@@ -25,28 +25,28 @@ export class UplStatuspageStatsgrid extends DeesElement {
public static demo = demoFunc; public static demo = demoFunc;
@property({ type: Number }) @property({ type: Number })
public uptime: number = 99.99; accessor uptime: number = 99.99;
@property({ type: Number }) @property({ type: Number })
public avgResponseTime: number = 125; accessor avgResponseTime: number = 125;
@property({ type: Number }) @property({ type: Number })
public totalIncidents: number = 0; accessor totalIncidents: number = 0;
@property({ type: Number }) @property({ type: Number })
public affectedServices: number = 0; accessor affectedServices: number = 0;
@property({ type: Number }) @property({ type: Number })
public totalServices: number = 0; accessor totalServices: number = 0;
@property({ type: String }) @property({ type: String })
public currentStatus: string = 'operational'; accessor currentStatus: string = 'operational';
@property({ type: Boolean }) @property({ type: Boolean })
public loading: boolean = false; accessor loading: boolean = false;
@property({ type: String }) @property({ type: String })
public timePeriod: string = '90 days'; accessor timePeriod: string = '90 days';
constructor() { constructor() {
super(); super();
@@ -54,32 +54,32 @@ export class UplStatuspageStatsgrid extends DeesElement {
public static styles = [ public static styles = [
domtools.elementBasic.staticStyles, domtools.elementBasic.staticStyles,
commonStyles, sharedStyles.commonStyles,
css` css`
:host { :host {
display: block; display: block;
background: transparent; background: transparent;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.container { .container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.stats-grid { .stats-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.stat-card { .stat-card {
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
padding: ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(sharedStyles.spacing.lg)};
transition: all 0.2s ease; transition: all 0.2s ease;
} }
@@ -97,10 +97,10 @@ export class UplStatuspageStatsgrid extends DeesElement {
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
font-weight: 500; font-weight: 500;
margin-bottom: ${unsafeCSS(spacing.sm)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
display: flex; display: flex;
align-items: center; align-items: center;
gap: ${unsafeCSS(spacing.xs)}; gap: ${unsafeCSS(sharedStyles.spacing.xs)};
} }
.stat-value { .stat-value {
@@ -120,7 +120,7 @@ export class UplStatuspageStatsgrid extends DeesElement {
.stat-change { .stat-change {
font-size: 12px; font-size: 12px;
margin-top: ${unsafeCSS(spacing.xs)}; margin-top: ${unsafeCSS(sharedStyles.spacing.xs)};
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
@@ -141,14 +141,14 @@ export class UplStatuspageStatsgrid extends DeesElement {
.loading-skeleton { .loading-skeleton {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.skeleton-card { .skeleton-card {
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
padding: ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(sharedStyles.spacing.lg)};
height: 100px; height: 100px;
} }
@@ -157,7 +157,7 @@ export class UplStatuspageStatsgrid extends DeesElement {
width: 80px; width: 80px;
background: ${cssManager.bdTheme('#f3f4f6', '#27272a')}; background: ${cssManager.bdTheme('#f3f4f6', '#27272a')};
border-radius: 4px; border-radius: 4px;
margin-bottom: ${unsafeCSS(spacing.sm)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
animation: pulse 2s infinite; animation: pulse 2s infinite;
} }
@@ -183,37 +183,37 @@ export class UplStatuspageStatsgrid extends DeesElement {
} }
.status-indicator.operational { .status-indicator.operational {
background: ${colors.status.operational}; background: ${sharedStyles.colors.status.operational};
} }
.status-indicator.degraded { .status-indicator.degraded {
background: ${colors.status.degraded}; background: ${sharedStyles.colors.status.degraded};
} }
.status-indicator.partial_outage { .status-indicator.partial_outage {
background: ${colors.status.partial}; background: ${sharedStyles.colors.status.partial};
} }
.status-indicator.major_outage { .status-indicator.major_outage {
background: ${colors.status.major}; background: ${sharedStyles.colors.status.major};
} }
.status-indicator.maintenance { .status-indicator.maintenance {
background: ${colors.status.maintenance}; background: ${sharedStyles.colors.status.maintenance};
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.container { .container {
padding: 0 ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
} }
.stats-grid { .stats-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
} }
.stat-card { .stat-card {
padding: ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.stat-value { .stat-value {

View File

@@ -1,7 +1,7 @@
import { DeesElement, property, html, customElement, type TemplateResult, cssManager, css, unsafeCSS } from '@design.estate/dees-element'; import { DeesElement, property, html, customElement, type TemplateResult, cssManager, css, unsafeCSS } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import type { IOverallStatus } from '../interfaces/index.js'; import type { IOverallStatus } from '../interfaces/index.js';
import { fonts, colors } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import { demoFunc } from './upl-statuspage-statusbar.demo.js'; import { demoFunc } from './upl-statuspage-statusbar.demo.js';
declare global { declare global {
@@ -15,7 +15,7 @@ export class UplStatuspageStatusbar extends DeesElement {
public static demo = demoFunc; public static demo = demoFunc;
@property({ type: Object }) @property({ type: Object })
public overallStatus: IOverallStatus = { accessor overallStatus: IOverallStatus = {
status: 'operational', status: 'operational',
message: 'All Systems Operational', message: 'All Systems Operational',
lastUpdated: Date.now(), lastUpdated: Date.now(),
@@ -24,10 +24,10 @@ export class UplStatuspageStatusbar extends DeesElement {
}; };
@property({ type: Boolean }) @property({ type: Boolean })
public loading: boolean = false; accessor loading: boolean = false;
@property({ type: Boolean }) @property({ type: Boolean })
public expandable: boolean = true; accessor expandable: boolean = true;
constructor() { constructor() {
super(); super();
@@ -40,13 +40,13 @@ export class UplStatuspageStatusbar extends DeesElement {
padding: 0; padding: 0;
display: block; display: block;
background: transparent; background: transparent;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
} }
.statusbar-container { .statusbar-container {
margin: auto; margin: auto;
max-width: 1200px; max-width: 1200px;
padding: 24px 24px; padding: ${unsafeCSS(sharedStyles.spacing.lg)};
position: relative; position: relative;
} }
@@ -54,75 +54,144 @@ export class UplStatuspageStatusbar extends DeesElement {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
min-height: 56px; min-height: 64px;
padding: 16px 20px; padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
border-radius: 6px; border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
cursor: ${cssManager.bdTheme('default', 'default')}; cursor: default;
transition: all 0.2s ease; transition: all ${unsafeCSS(sharedStyles.durations.normal)} ${unsafeCSS(sharedStyles.easings.default)};
position: relative; position: relative;
overflow: hidden; overflow: hidden;
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 15px;
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; letter-spacing: -0.01em;
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; background: ${sharedStyles.colors.background.card};
box-shadow: ${cssManager.bdTheme('0 1px 2px 0 rgba(0, 0, 0, 0.05)', 'none')}; border: 1px solid ${sharedStyles.colors.border.default};
box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
}
.statusbar-inner::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
transition: background ${unsafeCSS(sharedStyles.durations.normal)} ${unsafeCSS(sharedStyles.easings.default)};
}
.statusbar-inner.operational::before {
background: ${sharedStyles.colors.status.operational};
}
.statusbar-inner.degraded::before {
background: ${sharedStyles.colors.status.degraded};
}
.statusbar-inner.partial_outage::before {
background: ${sharedStyles.colors.status.partial};
}
.statusbar-inner.major_outage::before {
background: ${sharedStyles.colors.status.major};
}
.statusbar-inner.maintenance::before {
background: ${sharedStyles.colors.status.maintenance};
} }
.statusbar-inner:hover { .statusbar-inner:hover {
background: ${cssManager.bdTheme('#f9fafb', '#0f0f0f')}; border-color: ${sharedStyles.colors.border.muted};
box-shadow: ${unsafeCSS(sharedStyles.shadows.base)};
} }
.status-indicator { .status-indicator {
width: 8px; width: 10px;
height: 8px; height: 10px;
border-radius: 50%; border-radius: 50%;
flex-shrink: 0; flex-shrink: 0;
position: relative;
}
.status-indicator::after {
content: '';
position: absolute;
inset: -3px;
border-radius: 50%;
opacity: 0.2;
} }
.statusbar-inner.operational .status-indicator { .statusbar-inner.operational .status-indicator {
background: ${colors.status.operational}; background: ${sharedStyles.colors.status.operational};
box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(22, 163, 74, 0.15)', 'rgba(34, 197, 94, 0.2)')};
}
.statusbar-inner.operational .status-indicator::after {
background: ${sharedStyles.colors.status.operational};
animation: pulse-ring 2s ease-out infinite;
} }
.statusbar-inner.degraded .status-indicator { .statusbar-inner.degraded .status-indicator {
background: ${colors.status.degraded}; background: ${sharedStyles.colors.status.degraded};
box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(217, 119, 6, 0.15)', 'rgba(251, 191, 36, 0.2)')};
} }
.statusbar-inner.partial_outage .status-indicator { .statusbar-inner.partial_outage .status-indicator {
background: ${colors.status.partial}; background: ${sharedStyles.colors.status.partial};
box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(220, 38, 38, 0.15)', 'rgba(248, 113, 113, 0.2)')};
} }
.statusbar-inner.major_outage .status-indicator { .statusbar-inner.major_outage .status-indicator {
background: ${colors.status.major}; background: ${sharedStyles.colors.status.major};
box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(185, 28, 28, 0.15)', 'rgba(239, 68, 68, 0.2)')};
animation: pulse-indicator 1s ease-in-out infinite;
} }
.statusbar-inner.maintenance .status-indicator { .statusbar-inner.maintenance .status-indicator {
background: ${colors.status.maintenance}; background: ${sharedStyles.colors.status.maintenance};
box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(37, 99, 235, 0.15)', 'rgba(96, 165, 250, 0.2)')};
}
@keyframes pulse-ring {
0% { transform: scale(1); opacity: 0.2; }
50% { transform: scale(1.5); opacity: 0; }
100% { transform: scale(1); opacity: 0; }
}
@keyframes pulse-indicator {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
} }
.status-content { .status-content {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: ${unsafeCSS(sharedStyles.spacing.md)};
flex: 1; flex: 1;
padding-left: ${unsafeCSS(sharedStyles.spacing.sm)};
} }
.status-main { .status-main {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${sharedStyles.colors.text.primary};
}
.status-message {
font-weight: 600;
} }
.status-details { .status-details {
font-size: 13px; font-size: 13px;
color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; font-weight: 400;
color: ${sharedStyles.colors.text.secondary};
} }
.loading-skeleton { .loading-skeleton {
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${sharedStyles.colors.background.card};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${sharedStyles.colors.border.default};
border-radius: 6px; border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
height: 56px; height: 64px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
@@ -135,45 +204,63 @@ export class UplStatuspageStatusbar extends DeesElement {
right: 0; right: 0;
bottom: 0; bottom: 0;
background: ${cssManager.bdTheme( background: ${cssManager.bdTheme(
'linear-gradient(90deg, transparent 0%, rgba(0,0,0,0.03) 50%, transparent 100%)', 'linear-gradient(90deg, transparent 0%, rgba(0,0,0,0.04) 50%, transparent 100%)',
'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.03) 50%, transparent 100%)' 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.04) 50%, transparent 100%)'
)}; )};
animation: loading 1.5s infinite; animation: loading 1.5s ${unsafeCSS(sharedStyles.easings.default)} infinite;
} }
@keyframes loading { @keyframes loading {
0% { 0% { transform: translateX(-100%); }
transform: translateX(-100%); 100% { transform: translateX(200%); }
}
100% {
transform: translateX(200%);
}
} }
.last-updated { .last-updated {
font-size: 12px; font-size: 12px;
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')}; color: ${sharedStyles.colors.text.muted};
white-space: nowrap; white-space: nowrap;
padding: 4px 10px;
background: ${sharedStyles.colors.background.muted};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
} }
@media (max-width: 640px) { .statusbar-inner:hover .last-updated {
background: ${cssManager.bdTheme('#e4e4e7', '#3f3f46')};
}
@media (max-width: 768px) {
.statusbar-container { .statusbar-container {
padding: 16px 16px; padding: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.statusbar-inner { .statusbar-inner {
font-size: 13px; flex-direction: column;
padding: 12px 16px; align-items: flex-start;
min-height: 48px; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
min-height: auto;
padding: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.status-content { .status-main {
gap: 10px; flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.last-updated {
align-self: flex-start;
}
}
@media (max-width: 640px) {
.statusbar-inner {
font-size: 14px;
} }
.status-indicator { .status-indicator {
width: 6px; width: 8px;
height: 6px; height: 8px;
} }
.last-updated { .last-updated {

View File

@@ -10,7 +10,7 @@ import {
unsafeCSS, unsafeCSS,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import type { IStatusHistoryPoint } from '../interfaces/index.js'; import type { IStatusHistoryPoint } from '../interfaces/index.js';
import { fonts, colors, shadows, borderRadius, spacing, commonStyles, getStatusColor } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import './internal/uplinternal-miniheading.js'; import './internal/uplinternal-miniheading.js';
import { demoFunc } from './upl-statuspage-statusdetails.demo.js'; import { demoFunc } from './upl-statuspage-statusdetails.demo.js';
@@ -26,22 +26,22 @@ export class UplStatuspageStatusdetails extends DeesElement {
public static demo = demoFunc; public static demo = demoFunc;
@property({ type: Array }) @property({ type: Array })
public historyData: IStatusHistoryPoint[] = []; accessor historyData: IStatusHistoryPoint[] = [];
@property({ type: Array }) @property({ type: Array })
public dataPoints: IStatusHistoryPoint[] = []; accessor dataPoints: IStatusHistoryPoint[] = [];
@property({ type: String }) @property({ type: String })
public serviceId: string = ''; accessor serviceId: string = '';
@property({ type: String }) @property({ type: String })
public serviceName: string = 'Service'; accessor serviceName: string = 'Service';
@property({ type: Boolean }) @property({ type: Boolean })
public loading: boolean = false; accessor loading: boolean = false;
@property({ type: Number }) @property({ type: Number })
public hoursToShow: number = 48; accessor hoursToShow: number = 48;
constructor() { constructor() {
super(); super();
@@ -49,20 +49,20 @@ export class UplStatuspageStatusdetails extends DeesElement {
public static styles = [ public static styles = [
plugins.domtools.elementBasic.staticStyles, plugins.domtools.elementBasic.staticStyles,
commonStyles, sharedStyles.commonStyles,
css` css`
:host { :host {
position: relative; position: relative;
display: block; display: block;
background: transparent; background: transparent;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.container { .container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.graph-wrapper { .graph-wrapper {
@@ -86,10 +86,10 @@ export class UplStatuspageStatusdetails extends DeesElement {
position: relative; position: relative;
display: flex; display: flex;
gap: 2px; gap: 2px;
padding: ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.sm)};
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
overflow: hidden; overflow: hidden;
height: 40px; height: 40px;
} }
@@ -138,10 +138,10 @@ export class UplStatuspageStatusdetails extends DeesElement {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 0; padding: 0;
margin-top: ${unsafeCSS(spacing.xs)}; margin-top: ${unsafeCSS(sharedStyles.spacing.xs)};
font-size: 10px; font-size: 10px;
color: ${cssManager.bdTheme('#9ca3af', '#71717a')}; color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
opacity: 0.8; opacity: 0.8;
} }
@@ -202,16 +202,16 @@ export class UplStatuspageStatusdetails extends DeesElement {
@media (max-width: 640px) { @media (max-width: 640px) {
.container { .container {
padding: 0 ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
} }
.graph-container { .graph-container {
padding: ${unsafeCSS(spacing.sm)}; padding: ${unsafeCSS(sharedStyles.spacing.sm)};
} }
.mainbox .barContainer { .mainbox .barContainer {
height: 32px; height: 32px;
padding: ${unsafeCSS(spacing.xs)}; padding: ${unsafeCSS(sharedStyles.spacing.xs)};
} }
.time-labels { .time-labels {
@@ -221,7 +221,7 @@ export class UplStatuspageStatusdetails extends DeesElement {
.stats-row { .stats-row {
font-size: 11px; font-size: 11px;
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
align-items: flex-start; align-items: flex-start;
} }
} }
@@ -235,7 +235,7 @@ export class UplStatuspageStatusdetails extends DeesElement {
<div class="mainbox"> <div class="mainbox">
${this.loading ? html` ${this.loading ? html`
<div class="graph-container"> <div class="graph-container">
<div class="barContainer" style="background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; border: 1px solid ${cssManager.bdTheme('#f3f4f6', '#1f1f1f')}; border-radius: ${borderRadius.base}; padding: ${spacing.sm}; height: 40px;"> <div class="barContainer" style="background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; border: 1px solid ${cssManager.bdTheme('#f3f4f6', '#1f1f1f')}; border-radius: ${sharedStyles.borderRadius.base}; padding: ${sharedStyles.spacing.sm}; height: 40px;">
<div class="loading-skeleton"> <div class="loading-skeleton">
${Array(this.hoursToShow).fill(0).map(() => html`<div class="skeleton-bar"></div>`)} ${Array(this.hoursToShow).fill(0).map(() => html`<div class="skeleton-bar"></div>`)}
</div> </div>

View File

@@ -10,7 +10,7 @@ import {
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import type { IMonthlyUptime } from '../interfaces/index.js'; import type { IMonthlyUptime } from '../interfaces/index.js';
import { fonts, colors, shadows, borderRadius, spacing, commonStyles } from '../styles/shared.styles.js'; import * as sharedStyles from '../styles/shared.styles.js';
import './internal/uplinternal-miniheading.js'; import './internal/uplinternal-miniheading.js';
import { demoFunc } from './upl-statuspage-statusmonth.demo.js'; import { demoFunc } from './upl-statuspage-statusmonth.demo.js';
@@ -26,22 +26,22 @@ export class UplStatuspageStatusmonth extends DeesElement {
public static demo = demoFunc; public static demo = demoFunc;
@property({ type: Array }) @property({ type: Array })
public monthlyData: IMonthlyUptime[] = []; accessor monthlyData: IMonthlyUptime[] = [];
@property({ type: String }) @property({ type: String })
public serviceId: string = ''; accessor serviceId: string = '';
@property({ type: String }) @property({ type: String })
public serviceName: string = 'Service'; accessor serviceName: string = 'Service';
@property({ type: Boolean }) @property({ type: Boolean })
public loading: boolean = false; accessor loading: boolean = false;
@property({ type: Boolean }) @property({ type: Boolean })
public showTooltip: boolean = true; accessor showTooltip: boolean = true;
@property({ type: Number }) @property({ type: Number })
public monthsToShow: number = 5; accessor monthsToShow: number = 5;
constructor() { constructor() {
super(); super();
@@ -49,32 +49,32 @@ export class UplStatuspageStatusmonth extends DeesElement {
public static styles = [ public static styles = [
domtools.elementBasic.staticStyles, domtools.elementBasic.staticStyles,
commonStyles, sharedStyles.commonStyles,
css` css`
:host { :host {
position: relative; position: relative;
display: block; display: block;
background: transparent; background: transparent;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${colors.text.primary}; color: ${sharedStyles.colors.text.primary};
} }
.container { .container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.mainbox { .mainbox {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: ${unsafeCSS(spacing.lg)}; gap: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.statusMonth { .statusMonth {
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
padding: ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(sharedStyles.spacing.lg)};
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
position: relative; position: relative;
transition: all 0.2s ease; transition: all 0.2s ease;
@@ -92,7 +92,7 @@ export class UplStatuspageStatusmonth extends DeesElement {
.month-header { .month-header {
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
margin-bottom: ${unsafeCSS(spacing.md)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
letter-spacing: 0.02em; letter-spacing: 0.02em;
text-transform: uppercase; text-transform: uppercase;
@@ -102,7 +102,7 @@ export class UplStatuspageStatusmonth extends DeesElement {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: ${unsafeCSS(spacing.lg)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
} }
.days-grid { .days-grid {
@@ -119,7 +119,7 @@ export class UplStatuspageStatusmonth extends DeesElement {
font-weight: 500; font-weight: 500;
height: 20px; height: 20px;
line-height: 20px; line-height: 20px;
margin-bottom: ${unsafeCSS(spacing.sm)}; margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
text-transform: uppercase; text-transform: uppercase;
} }
@@ -171,7 +171,7 @@ export class UplStatuspageStatusmonth extends DeesElement {
.overall-uptime { .overall-uptime {
font-size: 12px; font-size: 12px;
margin-top: auto; margin-top: auto;
padding-top: ${unsafeCSS(spacing.md)}; padding-top: ${unsafeCSS(sharedStyles.spacing.md)};
color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -195,7 +195,7 @@ export class UplStatuspageStatusmonth extends DeesElement {
.loading-skeleton { .loading-skeleton {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: ${unsafeCSS(spacing.sm)}; gap: ${unsafeCSS(sharedStyles.spacing.sm)};
height: 100%; height: 100%;
} }
@@ -264,28 +264,28 @@ export class UplStatuspageStatusmonth extends DeesElement {
.no-data-message { .no-data-message {
grid-column: 1 / -1; grid-column: 1 / -1;
text-align: center; text-align: center;
padding: ${unsafeCSS(spacing['2xl'])}; padding: ${unsafeCSS(sharedStyles.spacing['2xl'])};
color: ${colors.text.secondary}; color: ${sharedStyles.colors.text.secondary};
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.container { .container {
padding: 0 ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)}; padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
} }
.mainbox { .mainbox {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: ${unsafeCSS(spacing.md)}; gap: ${unsafeCSS(sharedStyles.spacing.md)};
} }
.statusMonth { .statusMonth {
padding: ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.md)};
min-height: 260px; min-height: 260px;
} }
.loading-skeleton { .loading-skeleton {
height: 180px; height: 180px;
padding: ${unsafeCSS(spacing.md)}; padding: ${unsafeCSS(sharedStyles.spacing.md)};
} }
} }
` `

View File

@@ -7,6 +7,11 @@ export const statuspageAllgreen = () => html`
min-height: 100vh; min-height: 100vh;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
} }
.demo-page-wrapper > dees-demowrapper {
display: flex;
flex-direction: column;
gap: 24px;
}
</style> </style>
<div class="demo-page-wrapper"> <div class="demo-page-wrapper">

View File

@@ -7,6 +7,11 @@ export const statuspageDemo = () => html`
min-height: 100vh; min-height: 100vh;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
} }
.demo-page-wrapper > dees-demowrapper {
display: flex;
flex-direction: column;
gap: 24px;
}
</style> </style>
<div class="demo-page-wrapper"> <div class="demo-page-wrapper">

View File

@@ -7,6 +7,11 @@ export const statuspageMaintenance = () => html`
min-height: 100vh; min-height: 100vh;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
} }
.demo-page-wrapper > dees-demowrapper {
display: flex;
flex-direction: column;
gap: 24px;
}
</style> </style>
<div class="demo-page-wrapper"> <div class="demo-page-wrapper">

View File

@@ -7,6 +7,11 @@ export const statuspageOutage = () => html`
min-height: 100vh; min-height: 100vh;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
} }
.demo-page-wrapper > dees-demowrapper {
display: flex;
flex-direction: column;
gap: 24px;
}
</style> </style>
<div class="demo-page-wrapper"> <div class="demo-page-wrapper">

View File

@@ -8,49 +8,63 @@ export const fonts = {
export const colors = { export const colors = {
// Background colors // Background colors
background: { background: {
primary: cssManager.bdTheme('#ffffff', '#0a0a0a'), primary: cssManager.bdTheme('#ffffff', '#09090b'),
secondary: cssManager.bdTheme('#f9fafb', '#18181b'), secondary: cssManager.bdTheme('#fafafa', '#18181b'),
muted: cssManager.bdTheme('#f3f4f6', '#27272a'), muted: cssManager.bdTheme('#f4f4f5', '#27272a'),
card: cssManager.bdTheme('#ffffff', '#18181b') card: cssManager.bdTheme('#ffffff', '#0f0f12'),
elevated: cssManager.bdTheme('#ffffff', '#1a1a1e')
}, },
// Border colors // Border colors
border: { border: {
default: cssManager.bdTheme('#e5e7eb', '#27272a'), default: cssManager.bdTheme('#e4e4e7', '#27272a'),
muted: cssManager.bdTheme('#f3f4f6', '#3f3f46') muted: cssManager.bdTheme('#f4f4f5', '#3f3f46'),
subtle: cssManager.bdTheme('#f0f0f2', '#1f1f23')
}, },
// Text colors // Text colors
text: { text: {
primary: cssManager.bdTheme('#0a0a0a', '#fafafa'), primary: cssManager.bdTheme('#09090b', '#fafafa'),
secondary: cssManager.bdTheme('#6b7280', '#a1a1aa'), secondary: cssManager.bdTheme('#71717a', '#a1a1aa'),
muted: cssManager.bdTheme('#9ca3af', '#71717a') muted: cssManager.bdTheme('#a1a1aa', '#71717a')
}, },
// Status colors - using bright colors for better visibility in both themes // Status colors - vibrant and accessible
status: { status: {
operational: cssManager.bdTheme('#22c55e', '#22c55e'), operational: cssManager.bdTheme('#16a34a', '#22c55e'),
degraded: cssManager.bdTheme('#fbbf24', '#fbbf24'), degraded: cssManager.bdTheme('#d97706', '#fbbf24'),
partial: cssManager.bdTheme('#f87171', '#f87171'), partial: cssManager.bdTheme('#dc2626', '#f87171'),
major: cssManager.bdTheme('#ef4444', '#ef4444'), major: cssManager.bdTheme('#b91c1c', '#ef4444'),
maintenance: cssManager.bdTheme('#60a5fa', '#60a5fa') maintenance: cssManager.bdTheme('#2563eb', '#60a5fa')
},
// Accent colors for interactive elements
accent: {
primary: cssManager.bdTheme('#09090b', '#fafafa'),
hover: cssManager.bdTheme('#18181b', '#e4e4e7'),
focus: cssManager.bdTheme('#3b82f6', '#60a5fa')
} }
}; };
export const shadows = { export const shadows = {
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', xs: '0 1px 2px 0 rgba(0, 0, 0, 0.03)',
base: '0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06)', sm: '0 1px 3px 0 rgba(0, 0, 0, 0.06), 0 1px 2px -1px rgba(0, 0, 0, 0.06)',
md: '0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)', base: '0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.05)',
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', md: '0 6px 12px -2px rgba(0, 0, 0, 0.08), 0 3px 7px -3px rgba(0, 0, 0, 0.05)',
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)' lg: '0 12px 24px -4px rgba(0, 0, 0, 0.1), 0 6px 12px -6px rgba(0, 0, 0, 0.05)',
xl: '0 24px 48px -12px rgba(0, 0, 0, 0.12), 0 12px 24px -12px rgba(0, 0, 0, 0.05)',
inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.04)',
glow: '0 0 20px -5px rgba(34, 197, 94, 0.3)'
}; };
export const borderRadius = { export const borderRadius = {
xs: '3px',
sm: '4px', sm: '4px',
base: '6px', base: '6px',
md: '8px', md: '8px',
lg: '12px', lg: '12px',
xl: '16px', xl: '16px',
'2xl': '24px',
full: '9999px' full: '9999px'
}; };
@@ -61,7 +75,24 @@ export const spacing = {
lg: '24px', lg: '24px',
xl: '32px', xl: '32px',
'2xl': '48px', '2xl': '48px',
'3xl': '64px' '3xl': '64px',
'4xl': '96px'
};
// Animation easings
export const easings = {
default: 'cubic-bezier(0.4, 0, 0.2, 1)',
smooth: 'cubic-bezier(0.4, 0, 0.6, 1)',
bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
snappy: 'cubic-bezier(0.2, 0, 0, 1)'
};
// Durations
export const durations = {
fast: '100ms',
normal: '200ms',
slow: '300ms',
slower: '500ms'
}; };
export const commonStyles = css` export const commonStyles = css`
@@ -71,58 +102,102 @@ export const commonStyles = css`
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-family: ${unsafeCSS(fonts.base)}; font-family: ${unsafeCSS(fonts.base)};
font-size: 14px; font-size: 13px;
font-weight: 500; font-weight: 500;
height: 36px; height: 36px;
padding: 0 16px; padding: 0 14px;
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(borderRadius.base)};
border: 1px solid ${colors.border.default}; border: 1px solid ${colors.border.default};
background: transparent; background: ${colors.background.primary};
color: ${colors.text.primary}; color: ${colors.text.primary};
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
transition: all 0.2s ease; transition: all ${unsafeCSS(durations.normal)} ${unsafeCSS(easings.default)};
gap: 8px; gap: 6px;
white-space: nowrap;
letter-spacing: -0.01em;
} }
.button:hover { .button:hover {
background: ${cssManager.bdTheme('#f9fafb', '#262626')}; background: ${colors.background.secondary};
border-color: ${cssManager.bdTheme('#d1d5db', '#404040')}; border-color: ${cssManager.bdTheme('#d4d4d8', '#3f3f46')};
transform: translateY(-1px); box-shadow: ${unsafeCSS(shadows.xs)};
} }
.button:active { .button:active {
transform: translateY(0); transform: scale(0.98);
transition-duration: ${unsafeCSS(durations.fast)};
}
.button:focus-visible {
outline: 2px solid ${colors.accent.focus};
outline-offset: 2px;
} }
.button.primary { .button.primary {
background: ${colors.text.primary}; background: ${colors.accent.primary};
color: ${colors.background.primary}; color: ${colors.background.primary};
border-color: ${colors.text.primary}; border-color: transparent;
} }
.button.primary:hover { .button.primary:hover {
background: ${cssManager.bdTheme('#262626', '#f4f4f5')}; background: ${colors.accent.hover};
border-color: ${cssManager.bdTheme('#262626', '#f4f4f5')}; box-shadow: ${unsafeCSS(shadows.sm)};
}
.button.ghost {
background: transparent;
border-color: transparent;
}
.button.ghost:hover {
background: ${colors.background.muted};
}
.button.sm {
height: 32px;
padding: 0 12px;
font-size: 12px;
}
.button.lg {
height: 44px;
padding: 0 20px;
font-size: 15px;
} }
/* Card styles */ /* Card styles */
.card { .card {
background: ${colors.background.card}; background: ${colors.background.card};
border: 1px solid ${colors.border.default}; border: 1px solid ${colors.border.default};
border-radius: ${unsafeCSS(borderRadius.md)}; border-radius: ${unsafeCSS(borderRadius.lg)};
padding: ${unsafeCSS(spacing.lg)}; padding: ${unsafeCSS(spacing.lg)};
box-shadow: ${unsafeCSS(shadows.sm)}; box-shadow: ${unsafeCSS(shadows.sm)};
transition: all ${unsafeCSS(durations.normal)} ${unsafeCSS(easings.default)};
}
.card:hover {
border-color: ${colors.border.muted};
box-shadow: ${unsafeCSS(shadows.base)};
}
.card.interactive {
cursor: pointer;
}
.card.interactive:hover {
transform: translateY(-2px);
box-shadow: ${unsafeCSS(shadows.md)};
} }
/* Loading skeleton */ /* Loading skeleton */
.skeleton { .skeleton {
background: ${cssManager.bdTheme( background: ${cssManager.bdTheme(
'linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%)', 'linear-gradient(90deg, #f4f4f5 0%, #e4e4e7 50%, #f4f4f5 100%)',
'linear-gradient(90deg, #1f1f1f 25%, #262626 50%, #1f1f1f 75%)' 'linear-gradient(90deg, #18181b 0%, #27272a 50%, #18181b 100%)'
)}; )};
background-size: 200% 100%; background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite; animation: skeleton-loading 1.5s ease-in-out infinite;
border-radius: ${unsafeCSS(borderRadius.base)}; border-radius: ${unsafeCSS(borderRadius.base)};
} }
@@ -131,6 +206,32 @@ export const commonStyles = css`
100% { background-position: -200% 0; } 100% { background-position: -200% 0; }
} }
/* Pulse animation for status indicators */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Fade in animation */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn ${unsafeCSS(durations.slow)} ${unsafeCSS(easings.default)} forwards;
}
/* Scale in animation */
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.scale-in {
animation: scaleIn ${unsafeCSS(durations.slow)} ${unsafeCSS(easings.bounce)} forwards;
}
/* Container styles */ /* Container styles */
.container { .container {
max-width: 1200px; max-width: 1200px;
@@ -138,11 +239,96 @@ export const commonStyles = css`
padding: 0 ${unsafeCSS(spacing.lg)}; padding: 0 ${unsafeCSS(spacing.lg)};
} }
/* Status pill */
.status-pill {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: ${unsafeCSS(borderRadius.full)};
font-size: 12px;
font-weight: 500;
letter-spacing: 0.01em;
}
.status-pill .status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
}
.status-pill.operational {
background: ${cssManager.bdTheme('rgba(22, 163, 74, 0.1)', 'rgba(34, 197, 94, 0.15)')};
color: ${cssManager.bdTheme('#15803d', '#4ade80')};
}
.status-pill.operational .status-dot {
background: ${colors.status.operational};
}
.status-pill.degraded {
background: ${cssManager.bdTheme('rgba(217, 119, 6, 0.1)', 'rgba(251, 191, 36, 0.15)')};
color: ${cssManager.bdTheme('#b45309', '#fcd34d')};
}
.status-pill.degraded .status-dot {
background: ${colors.status.degraded};
}
.status-pill.partial_outage,
.status-pill.major_outage {
background: ${cssManager.bdTheme('rgba(220, 38, 38, 0.1)', 'rgba(248, 113, 113, 0.15)')};
color: ${cssManager.bdTheme('#b91c1c', '#fca5a5')};
}
.status-pill.partial_outage .status-dot,
.status-pill.major_outage .status-dot {
background: ${colors.status.major};
}
.status-pill.maintenance {
background: ${cssManager.bdTheme('rgba(37, 99, 235, 0.1)', 'rgba(96, 165, 250, 0.15)')};
color: ${cssManager.bdTheme('#1d4ed8', '#93c5fd')};
}
.status-pill.maintenance .status-dot {
background: ${colors.status.maintenance};
}
/* Responsive utilities */ /* Responsive utilities */
@media (max-width: 1024px) {
.container {
padding: 0 ${unsafeCSS(spacing.md)};
}
}
@media (max-width: 640px) { @media (max-width: 640px) {
.container { .container {
padding: 0 ${unsafeCSS(spacing.md)}; padding: 0 ${unsafeCSS(spacing.md)};
} }
.button {
height: 40px;
padding: 0 16px;
}
.button.sm {
height: 36px;
}
}
/* Visually hidden (for accessibility) */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
} }
`; `;

View File

@@ -1,7 +1,5 @@
{ {
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022", "target": "ES2022",
"module": "NodeNext", "module": "NodeNext",
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",