Migrate components
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="Components\Shared\Icon.razor"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
25
src/BlazorApp/Components/AnchorLink/AnchorLink.razor
Normal file
25
src/BlazorApp/Components/AnchorLink/AnchorLink.razor
Normal file
@@ -0,0 +1,25 @@
|
||||
@* External link component *@
|
||||
|
||||
<a href="@Href"
|
||||
target="@Target"
|
||||
class="@CombinedClasses">@ChildContent</a>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Href { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Target { get; set; } = "_blank";
|
||||
|
||||
[Parameter]
|
||||
public string? CssClass { get; set; }
|
||||
|
||||
private string CombinedClasses => string.IsNullOrEmpty(CssClass)
|
||||
? "underline underline-offset-2"
|
||||
: $"underline underline-offset-2 {CssClass}";
|
||||
|
||||
}
|
||||
@@ -2,19 +2,66 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
<ResourcePreloader />
|
||||
<link rel="stylesheet" href="css/app.css" />
|
||||
<ImportMap />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<HeadOutlet />
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0"/>
|
||||
|
||||
<base href="/"/>
|
||||
|
||||
<ResourcePreloader/>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet"
|
||||
href="css/app.css"/>
|
||||
|
||||
<ImportMap/>
|
||||
|
||||
<!-- Standard Favicons -->
|
||||
<link rel="icon"
|
||||
type="image/x-icon"
|
||||
href="images/favicon.ico"/>
|
||||
<link rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="images/favicon-16x16.png"/>
|
||||
<link rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="images/favicon-32x32.png"/>
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="images/apple-touch-icon.png"/>
|
||||
|
||||
<!-- Android/Chrome -->
|
||||
<link rel="manifest"
|
||||
href="site.webmanifest"/>
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor"
|
||||
content="#1a1a1a"/>
|
||||
<meta name="msapplication-config"
|
||||
content="browserconfig.xml"/>
|
||||
|
||||
<!-- Theme Color -->
|
||||
<meta name="theme-color"
|
||||
content="#1a1a1a"/>
|
||||
|
||||
<!-- Open Graph / Social Sharing -->
|
||||
<meta property="og:image"
|
||||
content="images/og-image.png"/>
|
||||
<meta property="og:image:width"
|
||||
content="1200"/>
|
||||
<meta property="og:image:height"
|
||||
content="630"/>
|
||||
|
||||
<HeadOutlet/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes />
|
||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||
<Routes/>
|
||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
10
src/BlazorApp/Components/Contact/Contact.razor
Normal file
10
src/BlazorApp/Components/Contact/Contact.razor
Normal file
@@ -0,0 +1,10 @@
|
||||
@* Contact call-to-action *@
|
||||
|
||||
<div class="contact-section">
|
||||
<Text>If you think I can help with your project...</Text>
|
||||
<a href="mailto:me@beaufindlay.com"
|
||||
class="contact-button">
|
||||
Get in touch
|
||||
<Icon Type="IconType.SendArrow"/>
|
||||
</a>
|
||||
</div>
|
||||
125
src/BlazorApp/Components/Icons/Icon.razor
Normal file
125
src/BlazorApp/Components/Icons/Icon.razor
Normal file
@@ -0,0 +1,125 @@
|
||||
@switch (Type)
|
||||
{
|
||||
case IconType.Github:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.LinkedIn:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.Email:
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.Code:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.Hosting:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 3h18v18H3V3zm16 16V5H5v14h14zm-8-2h2v-2h2v-2h-2v-2h2V9h-2V7h-2v2H9v2h2v2H9v2h2v2z"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.Blazor:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.5 2.5c-2.5 0-4.5 2-4.5 4.5v2.5l-2.5 2.5c-1.5 1.5-1.5 4 0 5.5s4 1.5 5.5 0L16.5 15V12.5c0-2.5 2-4.5 4.5-4.5V6c-2.5 0-4.5-2-4.5-4.5h-1zm-3 7.5v2l-1.5 1.5c-.8.8-2.2.8-3 0s-.8-2.2 0-3L9.5 9h3zm3-5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5-1.5-.7-1.5-1.5.7-1.5 1.5-1.5z"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.React:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12"
|
||||
cy="12"
|
||||
r="2"/>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>
|
||||
<ellipse cx="12"
|
||||
cy="12"
|
||||
rx="8"
|
||||
ry="3"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"/>
|
||||
<ellipse cx="12"
|
||||
cy="12"
|
||||
rx="3"
|
||||
ry="8"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.Database:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.Docker:
|
||||
<svg viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 2v7h7c0-3.87-3.13-7-7-7zm-2 0C7.13 2 4 5.13 4 9h7V2zM4 11c0 3.87 3.13 7 7 7v-7H4zm9 7c3.87 0 7-3.13 7-7h-7v7z"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.SendArrow:
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"/>
|
||||
</svg>
|
||||
break;
|
||||
case IconType.Menu:
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"/>
|
||||
</svg>
|
||||
break;
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public IconType Type { get; set; }
|
||||
|
||||
}
|
||||
16
src/BlazorApp/Components/Icons/IconType.cs
Normal file
16
src/BlazorApp/Components/Icons/IconType.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace BlazorApp.Components.Icons;
|
||||
|
||||
public enum IconType
|
||||
{
|
||||
Github,
|
||||
LinkedIn,
|
||||
Email,
|
||||
Code,
|
||||
Hosting,
|
||||
Blazor,
|
||||
React,
|
||||
Database,
|
||||
Docker,
|
||||
SendArrow,
|
||||
Menu
|
||||
}
|
||||
19
src/BlazorApp/Components/Icons/SocialIcons.razor
Normal file
19
src/BlazorApp/Components/Icons/SocialIcons.razor
Normal file
@@ -0,0 +1,19 @@
|
||||
@* Social media links *@
|
||||
|
||||
<div class="social-icons-container">
|
||||
<a href="https://github.com/bdfin"
|
||||
class="social-icon-link">
|
||||
<span class="sr-only">GitHub</span>
|
||||
<Icon Type="IconType.Github"/>
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/in/beau-findlay/"
|
||||
class="social-icon-link">
|
||||
<span class="sr-only">LinkedIn</span>
|
||||
<Icon Type="IconType.LinkedIn"/>
|
||||
</a>
|
||||
<a href="mailto:me@beaufindlay.com"
|
||||
class="social-icon-link">
|
||||
<span class="sr-only">Email</span>
|
||||
<Icon Type="IconType.Email"/>
|
||||
</a>
|
||||
</div>
|
||||
14
src/BlazorApp/Components/Layout/Footer.razor
Normal file
14
src/BlazorApp/Components/Layout/Footer.razor
Normal file
@@ -0,0 +1,14 @@
|
||||
@* Footer component *@
|
||||
|
||||
<div class="page-footer">
|
||||
<footer>
|
||||
<div class="footer-container">
|
||||
<div class="footer-content">
|
||||
<SocialIcons/>
|
||||
<p class="footer-text">
|
||||
© @DateTime.Now.Year Beau Findlay. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -1,17 +1,11 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<NavMenu />
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
<div class="px-6 lg:px-10">
|
||||
<div class="flex flex-col min-h-screen mx-auto max-w-7xl fade-in">
|
||||
<NavBar/>
|
||||
<div class="flex-1 py-8">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
70
src/BlazorApp/Components/Layout/NavBar.razor
Normal file
70
src/BlazorApp/Components/Layout/NavBar.razor
Normal file
@@ -0,0 +1,70 @@
|
||||
@* Navigation bar with mobile menu *@
|
||||
|
||||
<div class="navbar">
|
||||
<header>
|
||||
<nav aria-label="Global">
|
||||
<div class="logo-container">
|
||||
<a href="/"
|
||||
class="logo-link">
|
||||
<span class="sr-only">Beau Findlay</span>
|
||||
<img src="images/logo.webp"
|
||||
alt="Logo"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="mobile-menu-button-container">
|
||||
<label for="mobile-menu-toggle"
|
||||
class="menu-button">
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<Icon Type="IconType.Menu"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="desktop-nav">
|
||||
<NavLink href="/experience">Experience</NavLink>
|
||||
<NavLink href="/about">This app</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@* Mobile menu using CSS checkbox hack *@
|
||||
<input type="checkbox"
|
||||
id="mobile-menu-toggle"
|
||||
class="menu-toggle"/>
|
||||
<div class="mobile-menu-overlay"></div>
|
||||
<div class="mobile-menu-content">
|
||||
<div class="mobile-menu-inner">
|
||||
<div class="mobile-menu-header">
|
||||
<a href="/"
|
||||
class="logo-link">
|
||||
<span class="sr-only">Beau Findlay</span>
|
||||
<img src="images/logo.webp"
|
||||
alt="Logo"/>
|
||||
</a>
|
||||
<label for="mobile-menu-toggle"
|
||||
class="close-button">
|
||||
<span class="sr-only">Close menu</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mobile-menu-body">
|
||||
<div class="mobile-nav-links">
|
||||
<NavLink href="/experience">Experience</NavLink>
|
||||
<NavLink href="/about">This App</NavLink>
|
||||
</div>
|
||||
<div class="mobile-social-divider">
|
||||
<div class="mobile-social-container">
|
||||
<SocialIcons/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
@@ -1,22 +1,31 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">BlazorApp</a>
|
||||
<a class="navbar-brand"
|
||||
href="">BlazorApp</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
|
||||
<input type="checkbox"
|
||||
title="Navigation menu"
|
||||
class="navbar-toggler"/>
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<div class="nav-scrollable"
|
||||
onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="nav flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
|
||||
<NavLink class="nav-link"
|
||||
href=""
|
||||
Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill-nav-menu"
|
||||
aria-hidden="true"></span> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="weather">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
||||
<NavLink class="nav-link"
|
||||
href="weather">
|
||||
<span class="bi bi-list-nested-nav-menu"
|
||||
aria-hidden="true"></span> Weather
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
41
src/BlazorApp/Components/Pages/About.razor
Normal file
41
src/BlazorApp/Components/Pages/About.razor
Normal file
@@ -0,0 +1,41 @@
|
||||
@page "/about"
|
||||
|
||||
<PageTitle>Beau Findlay - About</PageTitle>
|
||||
|
||||
<Title CssClass="text-center pb-4">This App</Title>
|
||||
<Text>
|
||||
Below is an overview of how this simple app is made and what
|
||||
technologies are used. If you'd like to dive straight in, the full
|
||||
project is available on my
|
||||
<AnchorLink Href="https://github.com/bdfin/my-portfolio">
|
||||
GitHub
|
||||
</AnchorLink>.
|
||||
</Text>
|
||||
|
||||
<section>
|
||||
<Subtitle>App</Subtitle>
|
||||
|
||||
<Text>
|
||||
This app was originally made using
|
||||
<AnchorLink Href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">.NET Blazor WASM</AnchorLink>
|
||||
and then re-written in
|
||||
<AnchorLink Href="https://react.dev/">React</AnchorLink>
|
||||
with
|
||||
<AnchorLink Href="https://www.typescriptlang.org/">TypeScript</AnchorLink>
|
||||
as a learning exercise. I've now migrated it back to .NET Blazor to take advantage of static server-side
|
||||
rendering for maximum performance and to remove unnecessary dependencies on large JS and CSS libraries.
|
||||
</Text>
|
||||
<Text>
|
||||
This version uses pure vanilla CSS with CSS variables for theming, eliminating all JavaScript and external
|
||||
dependencies; the mobile menu uses a CSS checkbox hack for zero-JavaScript interactivity.
|
||||
</Text>
|
||||
</section>
|
||||
|
||||
<section class="mt-8">
|
||||
<Subtitle>Hosting & Deployment</Subtitle>
|
||||
|
||||
<Text>
|
||||
TODO: This section
|
||||
</Text>
|
||||
</section>
|
||||
|
||||
@@ -15,22 +15,28 @@
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that
|
||||
occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong>
|
||||
environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
107
src/BlazorApp/Components/Pages/Experience.razor
Normal file
107
src/BlazorApp/Components/Pages/Experience.razor
Normal file
@@ -0,0 +1,107 @@
|
||||
@page "/experience"
|
||||
|
||||
<PageTitle>Beau Findlay - Experience</PageTitle>
|
||||
|
||||
<Title CssClass="text-center">Experience</Title>
|
||||
|
||||
<p class="text-center text-xl font-semibold mb-10 ">
|
||||
Software Engineer since 2018
|
||||
</p>
|
||||
|
||||
<ol class="timeline">
|
||||
@foreach (var item in experienceTimelineItems)
|
||||
{
|
||||
<li class="timeline-item">
|
||||
<time class="timeline-date">
|
||||
@item.StartDate - @(item.EndDate ?? "Present")
|
||||
</time>
|
||||
<h3 class="timeline-title">
|
||||
@item.Title @("@") <AnchorLink Href="@item.CompanyUrl">@item.CompanyName</AnchorLink>
|
||||
</h3>
|
||||
@foreach (var content in item.Content)
|
||||
{
|
||||
<Text>@content</Text>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ol>
|
||||
|
||||
<Contact/>
|
||||
|
||||
@code {
|
||||
|
||||
private readonly List<WorkTimelineItem> experienceTimelineItems =
|
||||
[
|
||||
new()
|
||||
{
|
||||
StartDate = "September 2021",
|
||||
Title = "CTO",
|
||||
CompanyName = "un:hurd music",
|
||||
CompanyUrl = "https://unhurdmusic.com",
|
||||
Content =
|
||||
[
|
||||
"As one of the founding developers at un:hurd music and now Chief Technology Officer, I built and scaled un:hurd's back-end and cloud infrastructure that serves automated marketing soloutions for tens-of-thousands of artists and musicians.",
|
||||
"I lead a small but incredibly talented multi-disciplinary team building on the Azure cloud using a .NET backend, React web front-end and a Swift native iOS app."
|
||||
]
|
||||
},
|
||||
new()
|
||||
{
|
||||
StartDate = "August 2020",
|
||||
EndDate = "September 2021",
|
||||
Title = "Software Development Lead",
|
||||
CompanyName = "Vouch",
|
||||
CompanyUrl = "https://vouch.co.uk/",
|
||||
Content =
|
||||
[
|
||||
"At Vouch I lead the backend build of a new version of their tenant referencing software - an AI enhanced chat-bot based system utlising Azure Cognitive Services and various supporting serverless APIs written in .NET Core and hosted on Microsoft Azure."
|
||||
]
|
||||
},
|
||||
new()
|
||||
{
|
||||
StartDate = "May 2020",
|
||||
EndDate = "July 2020",
|
||||
Title = "Software Developer",
|
||||
CompanyName = "Paragon ID",
|
||||
CompanyUrl = "https://www.paragon-id.com/en",
|
||||
Content =
|
||||
[
|
||||
"I joined Paragon ID on a short-term contract where I wrote and deployed two key projects: A complex dashboard for a large construction equipment manufacturer to track assets across various manufacturing stages and a medical assets tracking dashboard deployed and used in multiple hospitals across the UK."
|
||||
]
|
||||
},
|
||||
new()
|
||||
{
|
||||
StartDate = "July 2019",
|
||||
EndDate = "May 2020",
|
||||
Title = "Software Developer",
|
||||
CompanyName = "Osborne Technologies",
|
||||
CompanyUrl = "https://www.osbornetechnologies.co.uk/",
|
||||
Content =
|
||||
[
|
||||
"I joined Osborne Technologies as the only cloud cloud-specialist and lead a project creating the first web-based version of their flag ship visitor management software utilising ASP.NET Core MVC and Microsoft SQL Server on the Microsoft Azure cloud."
|
||||
]
|
||||
},
|
||||
new()
|
||||
{
|
||||
StartDate = "September 2018",
|
||||
EndDate = "September 2019",
|
||||
Title = " MSc Computing Student",
|
||||
CompanyName = "Sheffield Hallam University",
|
||||
CompanyUrl = "https://www.shu.ac.uk/courses/computing/msc-computing/full-time",
|
||||
Content =
|
||||
[
|
||||
"I joined Sheffield Hallam University to study for a Master of Science in Computing. During my time there I completed modules in computer programming and web development, databases and big data, computer hardware, project management and my software development thesis; a .NET web application that compiles astronomy and space exploration data from various APIs into an accessible calendar."
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
private class WorkTimelineItem
|
||||
{
|
||||
public string StartDate { get; init; } = string.Empty;
|
||||
public string? EndDate { get; init; }
|
||||
public string Title { get; init; } = string.Empty;
|
||||
public string CompanyName { get; init; } = string.Empty;
|
||||
public string CompanyUrl { get; init; } = string.Empty;
|
||||
public string[] Content { get; init; } = [];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,18 @@
|
||||
@page "/"
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
<PageTitle>Beau Findlay - Home</PageTitle>
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
|
||||
Welcome to your new app.
|
||||
<Title CssClass="text-center mb-8">Hi, I'm Beau.</Title>
|
||||
<Text>
|
||||
I'm a UK-based software engineer and I love building cool stuff.
|
||||
</Text>
|
||||
<Text>
|
||||
I specialise in C#/.NET development and I've built systems that scale for hundreds-of-thousands of global users.
|
||||
</Text>
|
||||
<Text>
|
||||
I've worked with businesses at all sizes and stages and I'm currently heading up the tech as CTO at a cool startup called
|
||||
<AnchorLink Href="https://unhurdmusic.com">un:hurd music</AnchorLink>.
|
||||
</Text>
|
||||
<Text>
|
||||
I believe in a privacy-first, information-focussed and performant internet. You won't find any trackers, analytics or the need for a cookie consent policy here.
|
||||
</Text>
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
@page "/not-found"
|
||||
@layout MainLayout
|
||||
|
||||
<h3>Not Found</h3>
|
||||
<p>Sorry, the content you are looking for does not exist.</p>
|
||||
<PageTitle>Beau Findlay - Not Found</PageTitle>
|
||||
|
||||
<main class="grid min-h-full place-items-center px-6 py-24 sm:py-32 lg:px-8">
|
||||
<div class="text-center">
|
||||
<p class="text-base font-semibold">404</p>
|
||||
<h1 class="mt-4 text-4xl font-bold tracking-tight">
|
||||
Page not found
|
||||
</h1>
|
||||
<p class="mt-6 text-base leading-7">Sorry, this page doesn't exist.</p>
|
||||
</div>
|
||||
</main>
|
||||
@@ -1,64 +0,0 @@
|
||||
@page "/weather"
|
||||
@attribute [StreamRendering]
|
||||
|
||||
<PageTitle>Weather</PageTitle>
|
||||
|
||||
<h1>Weather</h1>
|
||||
|
||||
<p>This component demonstrates showing data.</p>
|
||||
|
||||
@if (forecasts == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th aria-label="Temperature in Celsius">Temp. (C)</th>
|
||||
<th aria-label="Temperature in Fahrenheit">Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
@code {
|
||||
private WeatherForecast[]? forecasts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Simulate asynchronous loading to demonstrate streaming rendering
|
||||
await Task.Delay(500);
|
||||
|
||||
var startDate = DateOnly.FromDateTime(DateTime.Now);
|
||||
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
|
||||
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = startDate.AddDays(index),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = summaries[Random.Shared.Next(summaries.Length)]
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private class WeatherForecast
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
public int TemperatureC { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
|
||||
@using BlazorApp.Components.Pages
|
||||
<Router AppAssembly="typeof(Program).Assembly"
|
||||
NotFoundPage="typeof(NotFound)">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||
<RouteView RouteData="routeData"
|
||||
DefaultLayout="typeof(MainLayout)"/>
|
||||
</Found>
|
||||
</Router>
|
||||
|
||||
17
src/BlazorApp/Components/Typography/Subtitle/Subtitle.razor
Normal file
17
src/BlazorApp/Components/Typography/Subtitle/Subtitle.razor
Normal file
@@ -0,0 +1,17 @@
|
||||
@* H2 heading component *@
|
||||
|
||||
<h2 class="@CombinedClasses">@ChildContent</h2>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? CssClass { get; set; }
|
||||
|
||||
private string CombinedClasses => string.IsNullOrEmpty(CssClass)
|
||||
? "flex items-center text-2xl py-4 font-semibold"
|
||||
: $"flex items-center text-2xl py-4 font-semibold {CssClass}";
|
||||
|
||||
}
|
||||
17
src/BlazorApp/Components/Typography/Text/Text.razor
Normal file
17
src/BlazorApp/Components/Typography/Text/Text.razor
Normal file
@@ -0,0 +1,17 @@
|
||||
@* Paragraph component *@
|
||||
|
||||
<p class="@CombinedClasses">@ChildContent</p>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? CssClass { get; set; }
|
||||
|
||||
private string CombinedClasses => string.IsNullOrEmpty(CssClass)
|
||||
? "text-paragraph"
|
||||
: $"text-paragraph {CssClass}";
|
||||
|
||||
}
|
||||
17
src/BlazorApp/Components/Typography/Title/Title.razor
Normal file
17
src/BlazorApp/Components/Typography/Title/Title.razor
Normal file
@@ -0,0 +1,17 @@
|
||||
@* H1 heading component *@
|
||||
|
||||
<h1 class="@CombinedClasses">@ChildContent</h1>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? CssClass { get; set; }
|
||||
|
||||
private string CombinedClasses => string.IsNullOrEmpty(CssClass)
|
||||
? "text-4xl py-4"
|
||||
: $"text-4xl py-4 {CssClass}";
|
||||
|
||||
}
|
||||
@@ -9,3 +9,9 @@
|
||||
@using BlazorApp
|
||||
@using BlazorApp.Components
|
||||
@using BlazorApp.Components.Layout
|
||||
@using BlazorApp.Components.Icons
|
||||
@using BlazorApp.Components.AnchorLink
|
||||
@using BlazorApp.Components.Contact
|
||||
@using BlazorApp.Components.Typography.Title
|
||||
@using BlazorApp.Components.Typography.Subtitle
|
||||
@using BlazorApp.Components.Typography.Text
|
||||
@@ -10,10 +10,11 @@ var app = builder.Build();
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
app.UseExceptionHandler("/Error", true);
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5064",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7162;http://localhost:5064",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5064",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7162;http://localhost:5064",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,109 +2,109 @@
|
||||
|
||||
/* Fade In */
|
||||
.fade-in {
|
||||
animation: fadeInAnimation ease 1s;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: forwards;
|
||||
animation: fadeInAnimation ease 1s;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeInAnimation {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Slide */
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutRight {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.fade-in-up {
|
||||
animation: fadeInUp var(--transition-slow) var(--transition-timing);
|
||||
animation: fadeInUp var(--transition-slow) var(--transition-timing);
|
||||
}
|
||||
|
||||
/* Spinner */
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 3px solid var(--color-slate-700);
|
||||
border-top-color: var(--color-slate-50);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 3px solid var(--color-slate-700);
|
||||
border-top-color: var(--color-slate-50);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pulse */
|
||||
.pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transitions */
|
||||
.transition {
|
||||
transition: all var(--transition-base) var(--transition-timing);
|
||||
transition: all var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
.transition-colors {
|
||||
transition: color var(--transition-base) var(--transition-timing),
|
||||
background-color var(--transition-base) var(--transition-timing),
|
||||
border-color var(--transition-base) var(--transition-timing);
|
||||
transition: color var(--transition-base) var(--transition-timing),
|
||||
background-color var(--transition-base) var(--transition-timing),
|
||||
border-color var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
.transition-transform {
|
||||
transition: transform var(--transition-base) var(--transition-timing);
|
||||
transition: transform var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
.transition-opacity {
|
||||
transition: opacity var(--transition-base) var(--transition-timing);
|
||||
transition: opacity var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
/* Hover */
|
||||
.hover-scale:hover {
|
||||
transform: scale(1.05);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
transform: translateY(-2px);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@import url('variables.css');
|
||||
@import url('reset.css');
|
||||
@import url('base.css');
|
||||
@import url('utilities.css');
|
||||
@import url('layout.css');
|
||||
@import url('components.css');
|
||||
@import url('animations.css');
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
/* Base Styles */
|
||||
|
||||
body {
|
||||
background-color: var(--color-black);
|
||||
color: var(--color-slate-50);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-size-base);
|
||||
line-height: var(--line-height-normal);
|
||||
background-color: var(--color-black);
|
||||
color: var(--color-slate-50);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-size-base);
|
||||
line-height: var(--line-height-normal);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
@@ -15,76 +17,78 @@ h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
line-height: var(--line-height-tight);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--font-size-3xl);
|
||||
font-size: var(--font-size-3xl);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--font-size-2xl);
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: var(--font-size-xl);
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
color: var(--color-slate-50);
|
||||
transition: color var(--transition-base) var(--transition-timing);
|
||||
color: var(--color-slate-50);
|
||||
display: inline-block;
|
||||
transition: transform var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-slate-300);
|
||||
transform: translateY(2px);
|
||||
color: var(--color-slate-50);
|
||||
}
|
||||
|
||||
a:focus-visible {
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Focus */
|
||||
*:focus-visible {
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
button:focus-visible,
|
||||
input:focus-visible,
|
||||
textarea:focus-visible,
|
||||
select:focus-visible {
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Selection */
|
||||
::selection {
|
||||
background-color: var(--color-slate-50);
|
||||
color: var(--color-black);
|
||||
background-color: var(--color-slate-50);
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
body::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-track {
|
||||
background: var(--color-white);
|
||||
background: var(--color-white);
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-thumb {
|
||||
background-color: var(--color-black);
|
||||
border: 1px solid var(--color-white);
|
||||
background-color: var(--color-black);
|
||||
border: 1px solid var(--color-white);
|
||||
}
|
||||
|
||||
body {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--color-black) var(--color-white);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--color-black) var(--color-white);
|
||||
}
|
||||
|
||||
@@ -2,213 +2,598 @@
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: var(--space-3) var(--space-6);
|
||||
font-weight: var(--font-weight-medium);
|
||||
text-align: center;
|
||||
border-radius: var(--radius-md);
|
||||
transition: all var(--transition-base) var(--transition-timing);
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: var(--space-3) var(--space-6);
|
||||
font-weight: var(--font-weight-medium);
|
||||
text-align: center;
|
||||
border-radius: var(--radius-md);
|
||||
transition: all var(--transition-base) var(--transition-timing);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:focus-visible {
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--color-slate-50);
|
||||
color: var(--color-black);
|
||||
background-color: var(--color-slate-50);
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--color-slate-200);
|
||||
background-color: var(--color-slate-200);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: transparent;
|
||||
color: var(--color-slate-50);
|
||||
border: 1px solid var(--color-slate-50);
|
||||
background-color: transparent;
|
||||
color: var(--color-slate-50);
|
||||
border: 1px solid var(--color-slate-50);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: var(--color-slate-50);
|
||||
color: var(--color-black);
|
||||
background-color: var(--color-slate-50);
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: var(--space-3);
|
||||
background-color: var(--color-black);
|
||||
border: 1px solid var(--color-slate-700);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-slate-50);
|
||||
font-size: var(--font-size-base);
|
||||
width: 100%;
|
||||
padding: var(--space-3);
|
||||
background-color: var(--color-black);
|
||||
border: 1px solid var(--color-slate-700);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-slate-50);
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-slate-50);
|
||||
outline: none;
|
||||
border-color: var(--color-slate-50);
|
||||
}
|
||||
|
||||
.input:invalid {
|
||||
border-color: #ef4444;
|
||||
border-color: #ef4444;
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
color: var(--color-slate-500);
|
||||
color: var(--color-slate-500);
|
||||
}
|
||||
|
||||
/* Textareas */
|
||||
.textarea {
|
||||
width: 100%;
|
||||
padding: var(--space-3);
|
||||
background-color: var(--color-black);
|
||||
border: 1px solid var(--color-slate-700);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-slate-50);
|
||||
font-size: var(--font-size-base);
|
||||
resize: vertical;
|
||||
min-height: 120px;
|
||||
width: 100%;
|
||||
padding: var(--space-3);
|
||||
background-color: var(--color-black);
|
||||
border: 1px solid var(--color-slate-700);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-slate-50);
|
||||
font-size: var(--font-size-base);
|
||||
resize: vertical;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-slate-50);
|
||||
outline: none;
|
||||
border-color: var(--color-slate-50);
|
||||
}
|
||||
|
||||
/* Labels */
|
||||
.label {
|
||||
display: block;
|
||||
margin-bottom: var(--space-2);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
display: block;
|
||||
margin-bottom: var(--space-2);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
background-color: var(--color-slate-900);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-6);
|
||||
border: 1px solid var(--color-slate-800);
|
||||
background-color: var(--color-slate-900);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-6);
|
||||
border: 1px solid var(--color-slate-800);
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--space-6) 0;
|
||||
/* NavBar */
|
||||
.navbar header {
|
||||
padding-top: var(--space-6);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
padding: var(--space-2) var(--space-4);
|
||||
color: var(--color-slate-50);
|
||||
font-size: var(--font-size-sm);
|
||||
transition: color var(--transition-base) var(--transition-timing);
|
||||
.navbar nav {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: flex;
|
||||
max-width: 80rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--color-slate-300);
|
||||
.navbar .logo-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav-link-active {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
.navbar .logo-link {
|
||||
margin: -0.375rem;
|
||||
padding: 0.375rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
padding: var(--space-8) 0;
|
||||
margin-top: auto;
|
||||
border-top: 1px solid var(--color-slate-800);
|
||||
text-align: center;
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-slate-400);
|
||||
.navbar .logo-link img {
|
||||
height: 4rem;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.navbar .mobile-menu-button-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navbar .menu-button {
|
||||
margin: -0.625rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.625rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navbar .menu-button svg {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.navbar .desktop-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar .desktop-nav a {
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Mobile Menu (CSS-only) */
|
||||
.mobile-menu-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-menu-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
font-size: var(--font-size-2xl);
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-menu-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
z-index: var(--z-overlay);
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
z-index: var(--z-overlay);
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 80%;
|
||||
max-width: 300px;
|
||||
background-color: var(--color-black);
|
||||
border-left: 1px solid var(--color-slate-800);
|
||||
padding: var(--space-6);
|
||||
z-index: var(--z-modal);
|
||||
transform: translateX(100%);
|
||||
transition: transform var(--transition-slow) var(--transition-timing);
|
||||
.menu-toggle:checked ~ .mobile-menu-overlay {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mobile-menu-toggle:checked ~ .mobile-menu-overlay {
|
||||
display: block;
|
||||
.mobile-menu-content {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: var(--z-modal);
|
||||
background-color: var(--color-black);
|
||||
width: 100%;
|
||||
padding: var(--space-6);
|
||||
color: white;
|
||||
transform: translateX(100%);
|
||||
transition: transform var(--transition-slow) var(--transition-timing);
|
||||
}
|
||||
|
||||
.mobile-menu-toggle:checked ~ .mobile-menu {
|
||||
display: block;
|
||||
transform: translateX(0);
|
||||
.menu-toggle:checked ~ .mobile-menu-content {
|
||||
display: block;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.mobile-menu-close {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: var(--space-6);
|
||||
font-size: var(--font-size-2xl);
|
||||
cursor: pointer;
|
||||
.mobile-menu-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mobile-menu-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
margin: -0.625rem;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.625rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-button svg {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.mobile-menu-body {
|
||||
margin-top: var(--space-6);
|
||||
flex: 1 1 0%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.mobile-nav-links {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.mobile-nav-links a {
|
||||
margin-left: -0.75rem;
|
||||
margin-right: -0.75rem;
|
||||
display: block;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 0.75rem;
|
||||
padding-top: var(--space-2);
|
||||
padding-bottom: var(--space-2);
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.mobile-social-divider {
|
||||
border-top: 1px solid rgba(226, 232, 240, 0.1);
|
||||
padding-top: var(--space-8);
|
||||
}
|
||||
|
||||
.mobile-social-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Prevent body scroll when mobile menu is open */
|
||||
.menu-toggle:checked ~ * {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body:has(.menu-toggle:checked) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.mobile-menu-content {
|
||||
max-width: 24rem;
|
||||
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
border-left: 2px solid var(--color-slate-800);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.navbar .logo-container {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.navbar .mobile-menu-button-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar .desktop-nav {
|
||||
display: flex;
|
||||
column-gap: var(--space-12);
|
||||
}
|
||||
|
||||
.mobile-menu-content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.page-footer footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.page-footer .footer-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-top: var(--space-8);
|
||||
padding-bottom: var(--space-8);
|
||||
}
|
||||
|
||||
.page-footer .footer-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.page-footer .footer-text {
|
||||
font-size: var(--font-size-xs);
|
||||
line-height: 1.25;
|
||||
color: var(--color-slate-50);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.page-footer .footer-content {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.page-footer .footer-text {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Social Icons */
|
||||
.social-icons-container {
|
||||
display: flex;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
.social-icon-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 40px;
|
||||
min-height: 40px;
|
||||
color: var(--color-slate-50);
|
||||
transition: transform var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
.social-icon-link svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.social-icon-link:hover {
|
||||
transform: translateY(2px);
|
||||
color: var(--color-slate-50);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.social-icons-container {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.social-icon-link {
|
||||
min-width: auto;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.social-icon-link svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tech Icons */
|
||||
.tech-icons-wrapper {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.tech-icons-title {
|
||||
font-size: var(--font-size-xl);
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-10);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.tech-icons-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-6);
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
||||
.tech-icon-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tech-icon-item svg {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.tech-icon-item p {
|
||||
margin-top: var(--space-2);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tech-icons-grid {
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* AboutTabs */
|
||||
.tabs-subtitle {
|
||||
font-weight: var(--font-weight-bold);
|
||||
font-size: var(--font-size-lg);
|
||||
margin-top: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
/* Contact Section */
|
||||
.contact-section {
|
||||
margin-bottom: var(--space-10);
|
||||
margin-top: var(--space-12);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
border: 1px solid var(--color-slate-700);
|
||||
background-color: var(--color-black);
|
||||
padding: 0.625rem 0.875rem;
|
||||
margin-top: var(--space-2);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-slate-50);
|
||||
transition: transform var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
.contact-button:hover {
|
||||
transform: translateY(2px);
|
||||
color: var(--color-slate-50);
|
||||
}
|
||||
|
||||
.contact-button:focus-visible {
|
||||
outline: 2px solid var(--color-slate-50);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.contact-button svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Text Paragraph */
|
||||
.text-paragraph {
|
||||
font-size: var(--font-size-lg);
|
||||
padding-top: var(--space-4);
|
||||
padding-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.text-paragraph {
|
||||
padding-top: var(--space-2);
|
||||
padding-bottom: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.link {
|
||||
color: var(--color-slate-50);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
color: var(--color-slate-50);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: var(--color-slate-300);
|
||||
color: var(--color-slate-300);
|
||||
}
|
||||
|
||||
/* Icon Buttons */
|
||||
.icon-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--space-2);
|
||||
transition: opacity var(--transition-base) var(--transition-timing);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--space-2);
|
||||
transition: opacity var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
.icon-button:hover {
|
||||
opacity: 0.7;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Timeline */
|
||||
.timeline {
|
||||
position: relative;
|
||||
border-left: 1px solid var(--color-slate-600);
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
margin-bottom: var(--space-10);
|
||||
margin-left: var(--space-4);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
position: absolute;
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
background-color: var(--color-slate-600);
|
||||
border-radius: 50%;
|
||||
left: -1.875rem;
|
||||
top: 0.375rem;
|
||||
border: 2px solid var(--color-black);
|
||||
}
|
||||
|
||||
.timeline-date {
|
||||
display: block;
|
||||
margin-bottom: var(--space-1);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: 1;
|
||||
color: var(--color-slate-400);
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
font-size: var(--font-size-2xl);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-slate-50);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
/* Tabs (CSS Radio Buttons) */
|
||||
.tabs-container {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
||||
.tab-radio {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-labels {
|
||||
display: flex;
|
||||
gap: var(--space-8);
|
||||
border-bottom: 1px solid var(--color-slate-700);
|
||||
margin-bottom: var(--space-10);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tab-label {
|
||||
padding: var(--space-6) 0;
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-slate-400);
|
||||
border-bottom: 4px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: all var(--transition-base) var(--transition-timing);
|
||||
}
|
||||
|
||||
.tab-label:hover {
|
||||
color: var(--color-slate-200);
|
||||
border-bottom-color: var(--color-slate-700);
|
||||
}
|
||||
|
||||
#tab0:checked ~ .tab-labels label[for="tab0"],
|
||||
#tab1:checked ~ .tab-labels label[for="tab1"],
|
||||
#tab2:checked ~ .tab-labels label[for="tab2"],
|
||||
#tab3:checked ~ .tab-labels label[for="tab3"] {
|
||||
color: var(--color-slate-200);
|
||||
border-bottom-color: var(--color-slate-300);
|
||||
}
|
||||
|
||||
.tab-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tab0:checked ~ .tab-panels #panel0,
|
||||
#tab1:checked ~ .tab-panels #panel1,
|
||||
#tab2:checked ~ .tab-panels #panel2,
|
||||
#tab3:checked ~ .tab-panels #panel3 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -2,239 +2,17 @@
|
||||
|
||||
/* Container */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: var(--container-max-width);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: var(--space-6);
|
||||
padding-right: var(--space-6);
|
||||
width: 100%;
|
||||
max-width: var(--container-max-width);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: var(--space-6);
|
||||
padding-right: var(--space-6);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding-left: var(--space-10);
|
||||
padding-right: var(--space-10);
|
||||
}
|
||||
}
|
||||
|
||||
/* Flexbox */
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.items-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.justify-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gap-1 {
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.gap-3 {
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.gap-4 {
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.gap-6 {
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
.gap-8 {
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.sm\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
.md\:grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
.lg\:grid-cols-5 {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Spacing */
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.p-2 { padding: var(--space-2); }
|
||||
.p-4 { padding: var(--space-4); }
|
||||
.p-6 { padding: var(--space-6); }
|
||||
.p-8 { padding: var(--space-8); }
|
||||
|
||||
.px-2 { padding-left: var(--space-2); padding-right: var(--space-2); }
|
||||
.px-4 { padding-left: var(--space-4); padding-right: var(--space-4); }
|
||||
.px-6 { padding-left: var(--space-6); padding-right: var(--space-6); }
|
||||
.px-8 { padding-left: var(--space-8); padding-right: var(--space-8); }
|
||||
|
||||
.py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); }
|
||||
.py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); }
|
||||
.py-6 { padding-top: var(--space-6); padding-bottom: var(--space-6); }
|
||||
.py-8 { padding-top: var(--space-8); padding-bottom: var(--space-8); }
|
||||
|
||||
.m-2 { margin: var(--space-2); }
|
||||
.m-4 { margin: var(--space-4); }
|
||||
.m-6 { margin: var(--space-6); }
|
||||
.m-8 { margin: var(--space-8); }
|
||||
|
||||
.mx-auto { margin-left: auto; margin-right: auto; }
|
||||
|
||||
.mt-2 { margin-top: var(--space-2); }
|
||||
.mt-4 { margin-top: var(--space-4); }
|
||||
.mt-6 { margin-top: var(--space-6); }
|
||||
.mt-8 { margin-top: var(--space-8); }
|
||||
|
||||
.mb-2 { margin-bottom: var(--space-2); }
|
||||
.mb-4 { margin-bottom: var(--space-4); }
|
||||
.mb-6 { margin-bottom: var(--space-6); }
|
||||
.mb-8 { margin-bottom: var(--space-8); }
|
||||
|
||||
/* Display */
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:flex {
|
||||
display: flex;
|
||||
}
|
||||
.lg\:hidden {
|
||||
display: none;
|
||||
}
|
||||
.lg\:block {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Widths */
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.max-w-sm {
|
||||
max-width: 24rem;
|
||||
}
|
||||
|
||||
.max-w-md {
|
||||
max-width: 28rem;
|
||||
}
|
||||
|
||||
.max-w-lg {
|
||||
max-width: 32rem;
|
||||
}
|
||||
|
||||
.max-w-xl {
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.max-w-2xl {
|
||||
max-width: 42rem;
|
||||
}
|
||||
|
||||
.max-w-7xl {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
/* Text */
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
.container {
|
||||
padding-left: var(--space-10);
|
||||
padding-right: var(--space-10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,30 +3,30 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-tab-size: 4;
|
||||
tab-size: 4;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-tab-size: 4;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
html:focus-within {
|
||||
scroll-behavior: smooth;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
line-height: 1.5;
|
||||
text-rendering: optimizeSpeed;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
min-height: 100vh;
|
||||
line-height: 1.5;
|
||||
text-rendering: optimizeSpeed;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
img,
|
||||
@@ -34,44 +34,44 @@ picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
list-style: none;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html:focus-within {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
html:focus-within {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
641
src/BlazorApp/wwwroot/css/utilities.css
Normal file
641
src/BlazorApp/wwwroot/css/utilities.css
Normal file
@@ -0,0 +1,641 @@
|
||||
/* Utility Classes */
|
||||
|
||||
/* Display */
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.inline-flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.flow-root {
|
||||
display: flow-root;
|
||||
}
|
||||
|
||||
/* Flexbox */
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.items-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.justify-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Gap */
|
||||
.gap-1 {
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.gap-3 {
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.gap-4 {
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.gap-6 {
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
.gap-8 {
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.place-items-center {
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
/* Spacing */
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.min-h-full {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.h-6 {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.h-16 {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.w-6 {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.w-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Max Widths */
|
||||
.max-w-sm {
|
||||
max-width: 24rem;
|
||||
}
|
||||
|
||||
.max-w-md {
|
||||
max-width: 28rem;
|
||||
}
|
||||
|
||||
.max-w-lg {
|
||||
max-width: 32rem;
|
||||
}
|
||||
|
||||
.max-w-xl {
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.max-w-2xl {
|
||||
max-width: 42rem;
|
||||
}
|
||||
|
||||
.max-w-4xl {
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.max-w-7xl {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
/* Padding */
|
||||
.p-2 {
|
||||
padding: var(--space-2);
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: var(--space-6);
|
||||
}
|
||||
|
||||
.p-8 {
|
||||
padding: var(--space-8);
|
||||
}
|
||||
|
||||
.p-1\.5 {
|
||||
padding: 0.375rem;
|
||||
}
|
||||
|
||||
.p-2\.5 {
|
||||
padding: 0.625rem;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
padding-left: var(--space-2);
|
||||
padding-right: var(--space-2);
|
||||
}
|
||||
|
||||
.px-3 {
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: var(--space-4);
|
||||
padding-right: var(--space-4);
|
||||
}
|
||||
|
||||
.px-6 {
|
||||
padding-left: var(--space-6);
|
||||
padding-right: var(--space-6);
|
||||
}
|
||||
|
||||
.px-8 {
|
||||
padding-left: var(--space-8);
|
||||
padding-right: var(--space-8);
|
||||
}
|
||||
|
||||
.py-2 {
|
||||
padding-top: var(--space-2);
|
||||
padding-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.py-4 {
|
||||
padding-top: var(--space-4);
|
||||
padding-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.py-6 {
|
||||
padding-top: var(--space-6);
|
||||
padding-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.py-8 {
|
||||
padding-top: var(--space-8);
|
||||
padding-bottom: var(--space-8);
|
||||
}
|
||||
|
||||
.py-24 {
|
||||
padding-top: 6rem;
|
||||
padding-bottom: 6rem;
|
||||
}
|
||||
|
||||
.pt-6 {
|
||||
padding-top: var(--space-6);
|
||||
}
|
||||
|
||||
.pt-8 {
|
||||
padding-top: var(--space-8);
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
/* Margins */
|
||||
.m-2 {
|
||||
margin: var(--space-2);
|
||||
}
|
||||
|
||||
.m-4 {
|
||||
margin: var(--space-4);
|
||||
}
|
||||
|
||||
.m-6 {
|
||||
margin: var(--space-6);
|
||||
}
|
||||
|
||||
.m-8 {
|
||||
margin: var(--space-8);
|
||||
}
|
||||
|
||||
.-m-1\.5 {
|
||||
margin: -0.375rem;
|
||||
}
|
||||
|
||||
.-m-2\.5 {
|
||||
margin: -0.625rem;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.-mx-3 {
|
||||
margin-left: -0.75rem;
|
||||
margin-right: -0.75rem;
|
||||
}
|
||||
|
||||
.-my-6 {
|
||||
margin-top: -1.5rem;
|
||||
margin-bottom: -1.5rem;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: var(--space-6);
|
||||
}
|
||||
|
||||
.mt-8 {
|
||||
margin-top: var(--space-8);
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: var(--space-12);
|
||||
}
|
||||
|
||||
.mt-auto {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.mb-8 {
|
||||
margin-bottom: var(--space-8);
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: var(--space-10);
|
||||
}
|
||||
|
||||
.mb-12 {
|
||||
margin-bottom: var(--space-12);
|
||||
}
|
||||
|
||||
/* Space Between */
|
||||
.space-x-6 > * + * {
|
||||
margin-left: var(--space-6);
|
||||
}
|
||||
|
||||
.space-y-2 > * + * {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.space-y-10 > * + * {
|
||||
margin-top: var(--space-10);
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: var(--font-size-3xl);
|
||||
}
|
||||
|
||||
.text-4xl {
|
||||
font-size: var(--font-size-4xl);
|
||||
}
|
||||
|
||||
.font-normal {
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
.font-medium {
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.leading-tight {
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
.leading-normal {
|
||||
line-height: var(--line-height-normal);
|
||||
}
|
||||
|
||||
.leading-relaxed {
|
||||
line-height: var(--line-height-relaxed);
|
||||
}
|
||||
|
||||
.leading-6 {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.leading-7 {
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.tracking-tight {
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.underline-offset-2 {
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.underline-offset-4 {
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
/* Positioning */
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.inset-0 {
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.inset-y-0 {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
/* Border */
|
||||
.border-t {
|
||||
border-top: 1px solid rgba(226, 232, 240, 0.1);
|
||||
}
|
||||
|
||||
.divide-y > * + * {
|
||||
border-top: 1px solid rgba(226, 232, 240, 0.1);
|
||||
}
|
||||
|
||||
.rounded-lg {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.rounded-md {
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
/* Z-index */
|
||||
.z-10 {
|
||||
z-index: var(--z-dropdown);
|
||||
}
|
||||
|
||||
/* Overflow */
|
||||
.overflow-y-auto {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Screen Reader Only */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
/* Responsive Utilities */
|
||||
@media (min-width: 640px) {
|
||||
.sm\:max-w-sm {
|
||||
max-width: 24rem;
|
||||
}
|
||||
|
||||
.sm\:ring-1 {
|
||||
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.sm\:border-l-2 {
|
||||
border-left: 2px solid var(--color-slate-800);
|
||||
}
|
||||
|
||||
.sm\:py-32 {
|
||||
padding-top: 8rem;
|
||||
padding-bottom: 8rem;
|
||||
}
|
||||
|
||||
.sm\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.sm\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.md\:flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.md\:justify-evenly {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.md\:items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.md\:justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.md\:order-1 {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.md\:order-2 {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.md\:mt-0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.md\:py-3 {
|
||||
padding-top: var(--space-3);
|
||||
padding-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
.md\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.md\:grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.lg\:hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lg\:block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lg\:px-8 {
|
||||
padding-left: var(--space-8);
|
||||
padding-right: var(--space-8);
|
||||
}
|
||||
|
||||
.lg\:px-10 {
|
||||
padding-left: var(--space-10);
|
||||
padding-right: var(--space-10);
|
||||
}
|
||||
|
||||
.lg\:flex-1 {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
.lg\:gap-x-12 {
|
||||
column-gap: var(--space-12);
|
||||
}
|
||||
|
||||
.lg\:grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.lg\:grid-cols-5 {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@@ -1,97 +1,97 @@
|
||||
/* Design Tokens */
|
||||
|
||||
:root {
|
||||
/* Colors */
|
||||
--color-black: #000000;
|
||||
--color-white: #ffffff;
|
||||
--color-slate-50: #f8fafc;
|
||||
--color-slate-100: #f1f5f9;
|
||||
--color-slate-200: #e2e8f0;
|
||||
--color-slate-300: #cbd5e1;
|
||||
--color-slate-400: #94a3b8;
|
||||
--color-slate-500: #64748b;
|
||||
--color-slate-600: #475569;
|
||||
--color-slate-700: #334155;
|
||||
--color-slate-800: #1e293b;
|
||||
--color-slate-900: #0f172a;
|
||||
--color-gray-700: #374151;
|
||||
--color-gray-800: #1f2937;
|
||||
/* Colors */
|
||||
--color-black: #000000;
|
||||
--color-white: #ffffff;
|
||||
--color-slate-50: #f8fafc;
|
||||
--color-slate-100: #f1f5f9;
|
||||
--color-slate-200: #e2e8f0;
|
||||
--color-slate-300: #cbd5e1;
|
||||
--color-slate-400: #94a3b8;
|
||||
--color-slate-500: #64748b;
|
||||
--color-slate-600: #475569;
|
||||
--color-slate-700: #334155;
|
||||
--color-slate-800: #1e293b;
|
||||
--color-slate-900: #0f172a;
|
||||
--color-gray-700: #374151;
|
||||
--color-gray-800: #1f2937;
|
||||
|
||||
/* Typography */
|
||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--font-size-xs: 0.75rem;
|
||||
--font-size-sm: 0.875rem;
|
||||
--font-size-base: 1rem;
|
||||
--font-size-lg: 1.125rem;
|
||||
--font-size-xl: 1.25rem;
|
||||
--font-size-2xl: 1.5rem;
|
||||
--font-size-3xl: 1.875rem;
|
||||
--font-size-4xl: 2.25rem;
|
||||
--font-size-5xl: 3rem;
|
||||
--font-size-6xl: 3.75rem;
|
||||
--line-height-tight: 1.25;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-relaxed: 1.75;
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
/* Typography */
|
||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--font-size-xs: 0.75rem;
|
||||
--font-size-sm: 0.875rem;
|
||||
--font-size-base: 1rem;
|
||||
--font-size-lg: 1.125rem;
|
||||
--font-size-xl: 1.25rem;
|
||||
--font-size-2xl: 1.5rem;
|
||||
--font-size-3xl: 1.875rem;
|
||||
--font-size-4xl: 2.25rem;
|
||||
--font-size-5xl: 3rem;
|
||||
--font-size-6xl: 3.75rem;
|
||||
--line-height-tight: 1.25;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-relaxed: 1.75;
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
|
||||
/* Spacing */
|
||||
--space-0: 0;
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 0.75rem;
|
||||
--space-4: 1rem;
|
||||
--space-5: 1.25rem;
|
||||
--space-6: 1.5rem;
|
||||
--space-8: 2rem;
|
||||
--space-10: 2.5rem;
|
||||
--space-12: 3rem;
|
||||
--space-16: 4rem;
|
||||
--space-20: 5rem;
|
||||
--space-24: 6rem;
|
||||
/* Spacing */
|
||||
--space-0: 0;
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 0.75rem;
|
||||
--space-4: 1rem;
|
||||
--space-5: 1.25rem;
|
||||
--space-6: 1.5rem;
|
||||
--space-8: 2rem;
|
||||
--space-10: 2.5rem;
|
||||
--space-12: 3rem;
|
||||
--space-16: 4rem;
|
||||
--space-20: 5rem;
|
||||
--space-24: 6rem;
|
||||
|
||||
/* Breakpoints */
|
||||
--breakpoint-sm: 640px;
|
||||
--breakpoint-md: 768px;
|
||||
--breakpoint-lg: 1024px;
|
||||
--breakpoint-xl: 1280px;
|
||||
--breakpoint-2xl: 1536px;
|
||||
/* Breakpoints */
|
||||
--breakpoint-sm: 640px;
|
||||
--breakpoint-md: 768px;
|
||||
--breakpoint-lg: 1024px;
|
||||
--breakpoint-xl: 1280px;
|
||||
--breakpoint-2xl: 1536px;
|
||||
|
||||
/* Container */
|
||||
--container-max-width: 80rem;
|
||||
/* Container */
|
||||
--container-max-width: 80rem;
|
||||
|
||||
/* Border Radius */
|
||||
--radius-sm: 0.125rem;
|
||||
--radius-base: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--radius-xl: 0.75rem;
|
||||
--radius-2xl: 1rem;
|
||||
--radius-full: 9999px;
|
||||
/* Border Radius */
|
||||
--radius-sm: 0.125rem;
|
||||
--radius-base: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--radius-xl: 0.75rem;
|
||||
--radius-2xl: 1rem;
|
||||
--radius-full: 9999px;
|
||||
|
||||
/* Z-index */
|
||||
--z-base: 0;
|
||||
--z-dropdown: 10;
|
||||
--z-sticky: 20;
|
||||
--z-fixed: 30;
|
||||
--z-overlay: 40;
|
||||
--z-modal: 50;
|
||||
--z-popover: 60;
|
||||
--z-tooltip: 70;
|
||||
/* Z-index */
|
||||
--z-base: 0;
|
||||
--z-dropdown: 10;
|
||||
--z-sticky: 20;
|
||||
--z-fixed: 30;
|
||||
--z-overlay: 40;
|
||||
--z-modal: 50;
|
||||
--z-popover: 60;
|
||||
--z-tooltip: 70;
|
||||
|
||||
/* Transitions */
|
||||
--transition-fast: 150ms;
|
||||
--transition-base: 200ms;
|
||||
--transition-slow: 300ms;
|
||||
--transition-slower: 500ms;
|
||||
--transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
/* Transitions */
|
||||
--transition-fast: 150ms;
|
||||
--transition-base: 200ms;
|
||||
--transition-slow: 300ms;
|
||||
--transition-slower: 500ms;
|
||||
--transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
/* Shadows */
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-base: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
/* Shadows */
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-base: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user