Bläddra i källkod

add dark mode

Aneurin Barker Snook 11 månader sedan
förälder
incheckning
fd71fdf658
4 ändrade filer med 153 tillägg och 30 borttagningar
  1. 31 3
      index.html
  2. 37 0
      src/components/Color.ts
  3. 2 0
      src/main.ts
  4. 83 27
      src/style.scss

+ 31 - 3
index.html

@@ -13,7 +13,10 @@
     <main>
       <section id="top">
         <header>
-          <h1>Aneurin Barker Snook</h1>
+          <h1>
+            <span>Aneurin</span>
+            <span>Barker Snook</span>
+          </h1>
         </header>
 
         <p class="description">Software engineer extraordinaire</p>
@@ -115,7 +118,7 @@
         </header>
 
         <p>
-          I am currently offering <span class="availability">limited availability</span> to work.
+          I am currently offering <span class="cta">limited availability</span> to work.
         </p>
 
         <p>
@@ -149,11 +152,36 @@
         </ul>
       </section>
     </main>
-    <script type="module" src="/src/main.ts"></script>
+
+    <div x-data="color" id="color">
+      <button x-on:click="rotateColorScheme">
+        <template x-if="currentScheme === 'auto'">
+          <!-- clock @ https://heroicons.com/mini -->
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
+            <path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm.75-13a.75.75 0 0 0-1.5 0v5c0 .414.336.75.75.75h4a.75.75 0 0 0 0-1.5h-3.25V5Z" clip-rule="evenodd" />
+          </svg>
+        </template>
+        <template x-if="currentScheme === 'light'">
+          <!-- sun @ https://heroicons.com/mini -->
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
+            <path d="M10 2a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 10 2ZM10 15a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 10 15ZM10 7a3 3 0 1 0 0 6 3 3 0 0 0 0-6ZM15.657 5.404a.75.75 0 1 0-1.06-1.06l-1.061 1.06a.75.75 0 0 0 1.06 1.06l1.06-1.06ZM6.464 14.596a.75.75 0 1 0-1.06-1.06l-1.06 1.06a.75.75 0 0 0 1.06 1.06l1.06-1.06ZM18 10a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1 0-1.5h1.5A.75.75 0 0 1 18 10ZM5 10a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1 0-1.5h1.5A.75.75 0 0 1 5 10ZM14.596 15.657a.75.75 0 0 0 1.06-1.06l-1.06-1.061a.75.75 0 1 0-1.06 1.06l1.06 1.06ZM5.404 6.464a.75.75 0 0 0 1.06-1.06l-1.06-1.06a.75.75 0 1 0-1.061 1.06l1.06 1.06Z" />
+          </svg>
+        </template>
+        <template x-if="currentScheme === 'dark'">
+          <!-- moon @ https://heroicons.com/mini -->
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
+            <path fill-rule="evenodd" d="M7.455 2.004a.75.75 0 0 1 .26.77 7 7 0 0 0 9.958 7.967.75.75 0 0 1 1.067.853A8.5 8.5 0 1 1 6.647 1.921a.75.75 0 0 1 .808.083Z" clip-rule="evenodd" />
+          </svg>
+        </template>
+      </button>
+    </div>
+
     <div x-data="analytics">
       <template x-if="scriptUrl && websiteId">
         <script defer x-bind:src="scriptUrl" x-bind:data-website-id="websiteId"></script>
       </template>
     </div>
+
+    <script type="module" src="/src/main.ts"></script>
   </body>
 </html>

+ 37 - 0
src/components/Color.ts

@@ -0,0 +1,37 @@
+export type ColorScheme = 'auto' | 'dark' | 'light'
+
+export interface ColorState {
+  currentScheme: ColorScheme
+
+  rotateColorScheme(): void
+  setCurrentScheme(value: ColorScheme, save?: boolean): void
+}
+
+export default function Color(): ColorState {
+  const state: ColorState = {
+    currentScheme: 'auto',
+
+    setCurrentScheme(value, save) {
+      this.currentScheme = value
+      document.body.classList.remove('color-auto', 'color-dark', 'color-light')
+      document.body.classList.add(`color-${value}`)
+
+      if (save) localStorage?.setItem('prefer-color-scheme', value)
+    },
+
+    rotateColorScheme() {
+      if (this.currentScheme === 'auto') this.setCurrentScheme('light', true)
+      else if (this.currentScheme === 'light') this.setCurrentScheme('dark', true)
+      else this.setCurrentScheme('auto', true)
+    },
+  }
+
+  function loadColorScheme() {
+    const prev = localStorage?.getItem('prefer-color-scheme')
+    if (prev) state.setCurrentScheme(prev as ColorScheme)
+  }
+
+  loadColorScheme()
+
+  return state
+}

+ 2 - 0
src/main.ts

@@ -1,12 +1,14 @@
 import './style.scss'
 import Alpine from 'alpinejs'
 import Analytics from './components/Analytics'
+import Color from './components/Color'
 import Contact from './components/Contact'
 import Skills from './components/Skills'
 
 function main() {
   (window as unknown as { Alpine: typeof Alpine }).Alpine = Alpine
   Alpine.data('analytics', Analytics(import.meta.env.VITE_ANALYTICS_WEBSITE_ID || ''))
+  Alpine.data('color', Color)
   Alpine.data('contact', Contact)
   Alpine.data('skills', Skills)
   Alpine.start()

+ 83 - 27
src/style.scss

@@ -2,14 +2,33 @@ $bp-tablet: 600px;
 $bp-desktop: 900px;
 $bp-super: 1200px;
 
-:root {
-  font-family: 'Roboto Mono', 'Courier New', Courier, monospace;
-  font-size: 12pt;
+$link-border-width: 0.2rem;
 
+@mixin prefer-light {
   --bg-color: #e9e9e9;
   --border-color: #ccc;
   --cta-color: #e24350;
   --fg-color: #333333;
+  --tag-bg-color: #e9e9e9;
+}
+
+@mixin prefer-dark {
+  --bg-color: #333333;
+  --border-color: #999;
+  --cta-color: #e24350;
+  --fg-color: #e9e9e9;
+  --tag-bg-color: #e9e9e9;
+}
+
+:root {
+  font-family: 'Roboto Mono', 'Courier New', Courier, monospace;
+  font-size: 12pt;
+
+  @include prefer-light;
+
+  @media (prefers-color-scheme: dark) {
+    @include prefer-dark;
+  }
 }
 
 html,
@@ -19,10 +38,18 @@ body {
 
   padding: 0;
   margin: 0;
+
+  .color-light {
+    @include prefer-light;
+  }
+
+  .color-dark {
+    @include prefer-dark;
+  }
 }
 
 @mixin link($color: var(--cta-color)) {
-  border-bottom: 0.2em solid $color;
+  border-bottom: $link-border-width solid $color;
   color: $color;
   text-decoration: none;
 }
@@ -43,7 +70,7 @@ a {
 h1 {
   font-size: 2rem;
   font-weight: 700;
-  line-height: 1em;
+  line-height: 1.4em;
   margin: 0;
   padding: 0;
 }
@@ -82,23 +109,30 @@ li {
   line-height: 1.5rem;
 }
 
+.cta {
+  color: var(--cta-color);
+  text-transform: uppercase;
+}
+
 main {
   display: flex;
   flex-direction: column;
   gap: 2rem;
-  margin: 2rem;
+  padding: 2rem;
 }
 
 #top {
-  .description {
-    font-style: italic;
+  header h1 {
+    display: flex;
+    flex-direction: column;
+
+    @media (min-width: $bp-desktop) {
+      display: initial;
+    }
   }
-}
 
-#availability {
-  .availability {
-    @include link;
-    text-transform: uppercase;
+  .description {
+    font-style: italic;
   }
 }
 
@@ -108,6 +142,9 @@ main {
   }
 
   .tags {
+    $ph: 0.4rem;
+    $pv: 0.2rem;
+
     display: flex;
     flex-direction: row;
     flex-wrap: wrap;
@@ -117,18 +154,20 @@ main {
       border-radius: 0.3rem;
       border-width: 0;
       display: inline-block;
-      padding: 0.1rem 0.4rem;
+      padding: $pv $ph;
     }
 
     @mixin tag($bg, $fg) {
       background-color: $bg;
+      border-color: $bg;
       color: $fg;
 
       &.active {
-        background-color: var(--bg-color);
-        border-bottom: 0.2rem solid $bg;
-        border-radius: 0;
-        color: $bg;
+        @include link($bg);
+        background-color: var(--tag-bg-color);
+        border-bottom-left-radius: 0;
+        border-bottom-right-radius: 0;
+        padding-bottom: $pv - $link-border-width;
       }
     }
 
@@ -153,7 +192,7 @@ main {
     }
 
     [data-tag="knowledge"] {
-      @include tag(grey, black);
+      @include tag(salmon, black);
     }
 
     [data-tag="language"] {
@@ -165,23 +204,19 @@ main {
     }
 
     [data-tag="operations"] {
-      @include tag(sienna, white);
+      @include tag(orangered, white);
     }
 
     [data-tag="php"] {
-      @include tag(purple, white);
+      @include tag(indigo, white);
     }
 
     [data-tag="platform"] {
-      @include tag(indigo, white);
+      @include tag(cornflowerblue, white);
     }
 
     [data-tag="web3"] {
-      @include tag(black, white);
-    }
-
-    .tag.active {
-      font-style: italic;
+      @include tag(darkslategray, white);
     }
   }
 
@@ -216,3 +251,24 @@ main {
     }
   }
 }
+
+#color {
+  position: absolute;
+  right: 2rem;
+  top: 2rem;
+
+  button {
+    background: none;
+    border: none;
+    color: var(--fg-color);
+    cursor: pointer;
+    display: block;
+
+    svg {
+      display: block;
+      fill: var(--fg-color);
+      height: 100%;
+      width: 2rem;
+    }
+  }
+}