You are browsing Nuxt 2 docs. Go to Nuxt 3 docs, or learn more about Nuxt 2 Long Term Support.

← Voltar

Escurecendo com o modo de cor da Nuxt

O módulo @nuxtjs/color-mode é uma boa maneira de adicionar o modo escuro ao seu sítio. Porém não apenas faz isto, mudar de escuro para claro mas também para qualquer tema de cor (por exemplo, o modo sépia). Ele até tem deteção automática, é assim que ela escolherá o modo correto dependendo da aparência do seu sistema.

|
Escurecendo com o modo de cor da Nuxt

Visualizar a demonstração / Código-fonte

O módulo @nuxtjs/color-mode é uma boa maneira de adicionar o modo escuro ao seu sítio. Porém não apenas faz isto de mudar de escuro para claro mas também para qualquer tema de cor (por exemplo, o modo sépia). Ele até tem auto deteção é assim que ela escolherá o modo correto dependendo da aparência do seu sistema.

Como isto funciona

O módulo @nuxtjs/color-mode adiciona uma classe .${color}-mode ao marcador <html>. Ele funciona com qualquer algo de Nuxt, seja estático ou servidor e universal ou a renderização no lado do cliente. Ela deteta automaticamente o sistema color-mode, assim você não precisa mudar manualmente a cor.

Ele injeta um auxiliar $colorMode com:

  • preference: O atual modo de cor selecionado (pode ser 'system'), atualiza-o para mudar a modo de cor preferido do utilizador
  • value: Útil para saber qual modo de cor foi detetado quando $colorMode === 'system', você não deve atualizá-lo.
  • unknown: Útil para saber se, durante a renderização no lado do servidor ou geração estática, nós precisamos renderizar um ocupante (placeholder)

Vamos começar

Você pode trabalhar sobre um projeto já criado ou iniciar um completamente novo. Para este exemplo eu criei um novo projeto e adicionei alguns textos ilustrativos ao ficheiro index.vue na pasta pages.

index.vue
<template>
  <h1>Testing color mode</h1>
</template>

Instalar o módulo color-mode

Antes de tudo você precisa instalar o módulo como uma dependência no seu projeto em Nuxt.

Yarn
yarn add --dev @nuxtjs/color-mode
NPM
npm install --save-dev @nuxtjs/color-mode

Depois você precisa adicionar o módulo à secção buildModules do seu ficheiro nuxt.config.js.

Se você ainda não tiver ficheiro nuxt.config.js você pode criar um no diretório raiz e adicionar o código abaixo.
nuxt.config.js
export default {
  buildModules: ['@nuxtjs/color-mode']
}

Se você estiver usando uma versão da Nuxt abaixo da 2.9.0 você precisará adicioná-lo à propriedade modules no lugar da propriedade buildModules.

Para consultar qual versão da Nuxt você tem, você pode executar o comando yarn nuxt -v ou npm run nuxt -v

Adicionando os seus estilos de cor

Agora você precisa adicionar alguns estilos às suas classes de modo. Vamos adicionar um ficheiro main.css à nossa pasta de recursos assets. Nós utilizaremos variáveis de CSS para definir a cor de raiz a qual será o modo claro e depois definir as cores para modo escuro e sépia. Assim nós podemos adicionar alguns estilos aos marcadores de corpo e ligação (body e a).

assets/main.css
:root {
  --color: #243746;
  --color-primary: #158876;
  --color-secondary: #0e2233;
  --bg: #f3f5f4;
  --bg-secondary: #fff;
  --border-color: #ddd;
}

.dark-mode {
  --color: #ebf4f1;
  --color-primary: #41b38a;
  --color-secondary: #fdf9f3;
  --bg: #091a28;
  --bg-secondary: #071521;
  --border-color: #0d2538;
}

.sepia-mode {
  --color: #433422;
  --color-secondary: #504231;
  --bg: #f1e7d0;
  --bg-secondary: #eae0c9;
  --border-color: #ded0bf;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  background-color: var(--bg);
  color: var(--color);
  transition: background-color 0.3s;
}

a {
  color: var(--color-primary);
}

No sentido de usar este ficheiro de CSS em nossa aplicação nós precisamos registá-lo. Nós fazemos isto ao adicionar uma propriedade css ao nosso ficheiro de configuração e adicionando o ficheiro de css que já temos criado.

nuxt.config.js
css: ['@/assets/main.css']

Inspecionando o HTML

Agora se você iniciar o seu sítio com npm run dev ou npx nuxt dev você deve ver o modo escuro se o seu sistema já estiver definido para o modo escuro e se você inspecionar o código verá a classe adicionada ao seu marcador de html.

<html class="dark-mode">
  // you might have light-mode here
</html>

Usando as ferramentas de desenvolvedor mude o modo para o modo sépia e modo claro para ver os efeitos.

<html class="sepia-mode">
  <!-- ou -->
  <html class="light-mode">
    <!-- ou modo escoro se você já estiver vendo o claro -->
  </html>
</html>

Você também pode mudar a cor na consola ao digitar:

$nuxt.$colorMode.preference = 'sepia'

Criando um interruptor de color-mode

Obviamente mudar o modo nas ferramentas do desenvolvedor não é o que nós queremos então vamos criar um interruptor de modo de cor, assim os nossos utilizadores podem rapidamente mudar de uma cor para uma outra.

Vamos criar um componente chamado ColorModePicker e nós podemos adicionar uma lista de cores. Por agora nós podemos apenas imprimir a cor a partir do nosso v-for.

components/ColorModePicker.vue
<template>
  <div>
    <ul>
      <li v-for="color of colors" :key="color">
        {{color}}
      </li>
    </ul>
  </div>
</template>

E a nossa propriedade de dados nos deixa retornar um arranjo de cores para cada modo.

components/ColorModePicker.vue
<script>
  export default {
    data() {
      return {
        colors: ['system', 'light', 'dark', 'sepia']
      }
    }
  }
</script>

Importando o nosso componente

Vamos importar o nosso componente para dentro da nossa página index.vue, assim podemos ver o que está acontecendo.

pages/index.vue
<template>
  <ColorModePicker />
</template>

<script>
  import ColorModePicker from '@/components/ColorModePicker'

  export default {
    components: {
      ColorModePicker
    }
  }
</script>

E no nosso navegador por baixo de http://localhost:3000 você verá a nossa lista de cores.

Lista de cores

Adicionando um evento de clique para mudar nossas cores

Depois em nosso modelo de marcação nós podemos adicionar um evento de clique que tornará o $colorMode.preference igual a cor que vem dos nossos dados.

Nós pode usar o nosso auxiliar $colorMode que nós recebemos com o módulo color-mode. Quando o utilizador clicar o $colorMode.preference será definido para a cor vindo dos nossos dados.

components/ColorModePicker.vue
<li
  v-for="color of colors"
  :key="color"
  @click="$colorMode.preference = color"
></li>

Isto é de fato tudo o que você precisa no sentido de isto funcionar. Se vocÊ consultar no seu navegador você verá que ao clicar em quaisquer uma das cores o fundo está mudando. Ele provavelmente não terá um cursor de ponteiro, então se você pensar que isto não funciona, ele funciona, você apenas está acostumado a ver o cursor.

E se nós consultar no navegador nós podemos ver que ele funciona mas está muito feito. Vamos arrumá-lo um pouco.

Adicionando alguns ícones

Vamos adicionar alguns ícones. Você pode copiar os ícones daqui e colocá-los na sua pasta de recursos assets em uma nova pasta chamada icons.

Nós usaremos os nossos ícones como um componente e no sentido de fazer isto nós usaremos o módulo @nuxtjs/svg que permite-te importar os ficheiros .svg de várias maneiras dependendo consulta de recurso que você fornecer.

Primeiro você precisará instalá-lo.

yarn add --dev @nuxtjs/svg
# OU npm install --save-dev @nuxtjs/svg

Depois nós precisamos adicioná-lo ao seu nuxt.config.js

nuxt.config.js
buildModules: ['@nuxtjs/svg', '@nuxtjs/color-mode']

Importando os ficheiros SVG como componentes

Nós agora podemos importas estes ícones em svg como componentes usando a pergunta ?inline, assim eles são importados como SVGs em linha.

components/ColorModePicker.vue
<script>
  import IconSystem from '@/assets/icons/system.svg?inline'
  import IconLight from '@/assets/icons/light.svg?inline'
  import IconDark from '@/assets/icons/dark.svg?inline'
  import IconSepia from '@/assets/icons/sepia.svg?inline'

  export default {
    components: {
      IconSystem,
      IconLight,
      IconDark,
      IconSepia
    },
    // ... a propriedade de dados (data) estará aqui
</script>

Adicionando um componente dinâmico

Agora nós podemos usar um componente dinâmico o qual verificará qual ícone adicionar dependendo das cores no nosso arranjo de dados. Vamos substituir o texto {{color}} por este novo componente dentro da nossa <li>.

components/ColorModePicker.vue
<component :is="`icon-${color}`" />

Vamos mover o nosso evento de clique do nossa <li> para o nosso componente de ícone.

components/ColorModePicker.vue
<component :is="`icon-${color}`" @click="$colorMode.preference = color" />

Estilizando os nossos ícones

E vamos adicionar alguns estilos, assim nós pode ver os nossos ícones. Nós usaremos estilização isolada (scoped) e usaremos a classe feather. Se você olhar para dentro do seus ficheiros em svg você verá que os nossos ficheiros em SVG tem a classe feather, assim nós podemos usar esta classe para o estilizar. Nós também adicionaremos uma classe preferida (preferred) e selecionada (selected), assim nós sabemos qual delas tem sido selecionada e qual é a preferida.

components/ColorModePicker.vue
<style scoped>
  .feather {
    position: relative;
    top: 0px;
    cursor: pointer;
    padding: 7px;
    background-color: var(--bg-secondary);
    border: 2px solid var(--border-color);
    margin: 0;
    border-radius: 5px;
    transition: all 0.1s ease;
  }
  .feather:hover {
    top: -3px;
  }
  .feather.preferred {
    border-color: var(--color-primary);
    top: -3px;
  }
  .feather.selected {
    color: var(--color-primary);
  }
</style>

Você não verá muita diferença de imediato exceto que o ícones parecem um pouco melhor mas agora nós precisamos mostrar uma classe diferente para o nosso ícone preferido o qual vem do nosso sistema de preferência e aquela para o nosso ícone selecionado o qual é para quando nós usarmos o evento de clique para mudar a cor.

Criando um método para mostrar a nossa classe preferida

Para fazer isso nós podemos criar um método que retornará a classe que nós queremos. Nós podemos chamar o nosso método de getClasses e passar a cor como o parâmetro. As duas classes que nós queremos retornar são preferred e selected. A cor preferida (preferred) deve ser igual ao $colorMode.preference e a cor selecionada (selected) deve ser igual ao $colorMode.value. Se o colorMode for desconhecido nós podemos retornar um objeto vazio.

components/ColorModePicker.vue
data () {
  return {
    colors: ['system', 'light', 'dark', 'sepia']
  }
},
methods: {
  getClasses (color) {
    // Não definir classes na renderização no lado do servidor quando a preferência (`preference`) for sistema (`system`) (porque nós não sabemos a preferência até o lado do cliente)
    if (this.$colorMode.unknown) {
      return {}
    }
    return {
      preferred: color === this.$colorMode.preference,
      selected: color === this.$colorMode.value
    }
  }
}

Agora nós podemos adicionar esta classe ao nosso componente de ícone. A classe chamará o método getClasses passando a cor que nós recebemos quando nós usamos o evento de clique.

components/ColorModePicker.vue
<component
  :is="`icon-${color}`"
  @click="$colorMode.preference = color"
  :class="getClasses(color)"
/>

E agora você verá no navegador que as cores que estão sendo aplicadas como nós queríamos. Mas não está muito claro sobre o que está acontecendo quando clicamos no sistema de ícone.

Adicionando algum texto usando o componente ColorScheme

Vamos adicionar algo que ajude o utilizador a compreendê-lo.

Se você está fazendo a renderização no lado do servidor (nuxt start ou nuxt generate) e se $colorMode.preference está definido para 'system', usar o $coloMode em seu modelo de marcação de Vue levará um clarão. Isto é por causa do fato de que nós não podemos saber as preferências do utilizador quando estivermos pré-renderizando a página visto que elas são detetadas no lado do cliente.

Para evitar o clarão, nós temos que guardar qualquer caminho de renderização que dependa de $colorMode com o $colorMode.unknown para renderizar o segurador de lugar (placeholder) ou usar o nosso componente <ColorScheme>.

Vamos criar um componente ColorScheme dentro do nosso elemento <ul> com um segurador de lugar e um marcador de span. Dentro dele nós podemos adicionar algum texto e exibir o $colorMode.preference que nós recebemos do módulo color-mode.

components/ColorModePicker.vue
<ColorScheme placeholder="..." tag="span">
  Color mode: <b>{{ $colorMode.preference }}</b>
</ColorScheme>

Agora você verá no navegador que se você mudar o ícone, o texto aparecerá o que corresponde ao ícone clicado.

Adicionando um texto quando o sistema for escolhido

Nós podemos melhorar isto ainda mais ao ver quando a preferência é o sistema e adicionar uma outra mensagem que exibe qual valor foi detetado.

components/ColorModePicker.vue
<ColorScheme placeholder="..." tag="span">
  Color mode: <b>{{ $colorMode.preference }}</b>
  <span v-if="$colorMode.preference === 'system'"
    >(<i>{{ $colorMode.value }}</i> mode detected)</span
  >
</ColorScheme>

Se você testar isto no navegador, você verá que está parecendo-se muito bem e nós estamos quase lá.

Pondo os nossos estilos em ordem

Agora nós apenas temos que arrumar um pouco os estilos. Vamos remover os pontos da <ul> e adicionar algum espaçamento e algum estilo ao nosso marcador <p>.

components/ColorModePicker.vue
ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
ul li {
  display: inline-block;
  padding: 5px;
}
p {
  margin: 0;
  padding-top: 5px;
  padding-bottom: 20px;
}

E no sentido de centralizá-lo, nós podemos envolver o nosso componente ColorModePicker em uma div com a classe de container.

pages/index.vue
<div class="container">
  <ColorModePicker />
</div>

E adicionar os estilos à classe container nos nossos estilos.

pages/index.vue
<style scoped>
  .container {
    text-align: center;
    padding: 50px;
  }
</style>

Conclusão

E aí você tem o seu componente color-mode bonito e completamente funcional. Sinta livre para divertir-se um pouco mais ao mudar os ícones ou adicionar mais cores ou modificar o esquema de cor. Divirta-se.

O que fazer a seguir