Skip to content

Real Dark Mode util

Really dark (◐‿◑)

Inspired by this work.

Examples

Basic Usage

The light follows your cursor. When you press and hold, the light stops moving and points at the mouse position.

Use the v-util-real-dark directive to mark the elements that should cast shadows.

What is Dark Mode?

Dark Mode is a display mode that darkens the interface background and brightens the text.

The main purpose is to reduce the bright brightness of the screen in low light environments, reduce visual fatigue, and save a little electricity on some devices.

Another benefit is that when you can't sleep at night and want to sing your mood in the dark, avoid the phone turning into a flash grenade when you slide it open.

After enabling the dark mode, you will find:

  • Look more professional, but still can't leave early
  • Less eye strain when scrolling social media, but still get blinded by the flash

Above, now you know what black is (⌐■_■)✧

#Canvas #Shader

Bystander: I'm pretty sure your "dark" isn't the "dark" of dark mode Σ(ˊДˋ;)

View the example source code
vue
<template>
  <div class="w-full border border-gray-200 rounded-xl p-4">
    <div
      v-util-real-dark
      class="w-full border border-gray-200 rounded-xl"
    >
      <base-checkbox
        v-model="enable"
        :label="t('enableRealDarkMode')"
        class="p-4"
      />
    </div>

    <div class="w-full py-4">
      <div class="mb-3 text-2xl font-bold">
        {{ t('whatIsDarkMode') }}
      </div>

      <div class="text-base leading-relaxed space-y-3">
        <p>
          {{ t('darkModeDescription') }}
        </p>

        <p>
          {{ t('darkModePurpose') }}
        </p>

        <p>
          {{ t('darkModeBenefit') }}
        </p>

        <p>
          {{ t('darkModeAfterEnable') }}
        </p>

        <ul class="list-disc list-inside space-y-1">
          <li>{{ t('darkModeAfterEnableList1') }}</li>
          <li>{{ t('darkModeAfterEnableList2') }}</li>
        </ul>

        <p>
          {{ t('darkModeAfterEnableConclusion') }}
        </p>
      </div>

      <!-- tags -->
      <div class="mt-4 flex gap-2">
        <span
          v-util-real-dark
          class="border border-gray-200 rounded-full p-2 px-4 text-sm text-gray-500"
        >
          #Canvas
        </span>
        <span
          v-util-real-dark
          class="border border-gray-200 rounded-full p-2 px-4 text-sm text-gray-500"
        >
          #Shader
        </span>
      </div>
    </div>

    <util-real-dark
      class="fixed left-0 top-0 z-[100] h-full w-full"
      :disabled="!enable"
    />
  </div>
</template>

<script setup lang="ts">
import { useData } from 'vitepress'
import { onBeforeUnmount, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import BaseCheckbox from '../../base-checkbox.vue'
import UtilRealDark from '../util-real-dark.vue'
import { vUtilRealDark } from '../v-util-real-dark'

const { isDark } = useData()
const oriValue = isDark.value

const { t } = useI18n()
const enable = ref(false)

watch(enable, (value) => {
  if (value) {
    isDark.value = false
  }
  else {
    isDark.value = oriValue
  }
})

onBeforeUnmount(() => {
  isDark.value = oriValue
})
</script>

How it works

I originally implemented it with drawing-related libraries like p5.js, but recreating effects like light projection and bloom/glow was just too hard, so this component ended up sitting idle for quite a while...(›´ω`‹ )

One day I suddenly had an idea: “Don’t 3D engines already have light sources built in?” So I switched to implementing it with Babylon.js.

The key is using a shader to set alpha based on grayscale values—making brighter areas more transparent—to achieve the light-beam effect.

Finally, after adding the moving light source effect, it worked! ( •̀ ω •́ )✧

Source Code

API

Props

interface Props {
  disabled?: boolean;
  /** 平滑係數。越小越慢,延遲越明顯 */
  ease?: number;
  /** 甩尾強度。越大越明顯 */
  offsetStrength?: number;
  /** 燈光尺寸 */
  size?: number;
  lightColor?: string;
  /** 透明度 @default 0.9 */
  opacity?: number;
}

v0.55.1