Hoe u begint met het coderen van uw ontwerpsysteem in Vue

Stel je voor, je maakt je web-app voor dit nieuwe epische idee en implementeert de kernfunctionaliteit. Het ontwerp kan later komen denk je.

Nou later is nu, je krijgt grip en krijgt feedback, het is tijd om je ontwerp te herzien of volledig opnieuw te maken!

In dit artikel bespreek ik de tools en methoden die je kunt gebruiken om precies dat te bereiken.

Ik ben persoonlijk in deze situatie terechtgekomen met mijn web-app Mylo, een applicatie voor trainingsbeheer. Naarmate het groeide en in contact kwam met gebruikers, ontstonden er problemen met kleurcontrasten, inconsistenties en meer.

Het oplossen van deze problemen kan worden beschreven in twee pijlers:

  • Ontwerpsysteem (hoe alles eruit zal zien)
  • Implementatie (hoe de look toe te passen)

(Ik concentreer me hier op kijken, maar begrijp dat ontwerp slechts voor ongeveer 20% eruit ziet, de rest moet overwegingen zijn over de gebruikerservaring)

Door dit heen te gaan, heb ik veel geleerd, maar ook duidelijk gemaakt hoeveel er nog te leren is. Als je fouten vindt of tips hebt, laat het me dan weten :D

Ik zal TailwindCSS in dit artikel gebruiken, maar alles is net zo goed zonder.

Inhoudsopgave

  • Mapstructuur
  • Kleuren
  • Pictogrammen
  • Knoppen
  • Tekstinvoer
  • Radioknoppen
  • Invoer selecteren
  • Conclusie

Pijler 1:Ontwerpsysteem

De eerste stap, die tegenwoordig een rage is, is het hebben van een ontwerpsysteem. Een ontwerpsysteem is in wezen een visuele codebase van uw software, en net als een codebase is het een volledige en specifieke beschrijving van hoe de applicatie er onder bijna alle omstandigheden uit zou moeten zien.

En dus, hoe meer je onderzoekt waaruit een ontwerpsysteem bestaat, hoe meer het aanvoelt als een onmogelijke taak. Een compleet ontwerpsysteem omvat de kleuren, spatiëringsregels, tekststijlen, knoppen, containers, branding, toegankelijkheid en nog veel meer.

De beste en meest uitgebreide bron die ik heb gevonden is de Design System Checklist.

Daarom voel ik me meer op mijn gemak om te verwijzen naar wat ik heb als een ontwerpbibliotheek met losse richtlijnen. Het komt goed omdat ik voor ontwerpvragen gewoon naar mezelf kan verwijzen;)

We gaan dus bespreken hoe we elementen zoals knoppen, pictogrammen, kleuren en een paar invoertypen kunnen implementeren.

Pijler 2:Implementatie

Directorystructuur

Het coderen van ontwerpelementen is geweldig voor herbruikbaarheid en consistentie, maar het is niet erg handig als componenten zich overal in de app bevinden op willekeurige, moeilijk toegankelijke locaties. We willen dat ze georganiseerd zijn.

Ik raad aan om de kleinste/atomaire elementen in de src/components . te plaatsen map en vervolgens in hun eigen submappen (pictogrammen, ingangen, knoppen, enz.)
Samengestelde componenten, opgebouwd uit de kleinere, kunnen in de src/layouts . worden geplaatst map, opnieuw met hun eigen submappen.

Kleuren

Het definiëren en afdwingen van kleuren is een goede eerste stap.

Je hebt over het algemeen een duidelijke discrete set kleuren die je app toestaat, om ze binnen te brengen, kun je het TailwindCSS-configuratiebestand wijzigen of ze toevoegen als CSS-variabelen.

Zo ziet het eruit in TailwindCSS. Ik heb de standaardkleuren overschreven om het gebruik van de systeemkleuren af ​​te dwingen, maar je kunt ook het standaardthema uitbreiden en je eigen kleuren toevoegen.

// tailwind.config.js

module.exports = {
  theme: {
    colors: {
      white: '#FFFFFF',
      black: '#000000',
      gray: {
        default: '#EEEEEE',
        dark: '#5D5D5C',
        darker: '#414040'
      },
      green: {
        default: '#83E8BC',
        dark: '#56806C',
        darker: '#445F51'
      },
      red: {
        default: '#F25F5C',
        dark: '#C15450',
        darker: '#8B4440'
      }
    }
  }
}

TailwindCSS Docs - Themaconfiguratie

Als je geen rugwind gebruikt, kan dit ook worden bereikt met behulp van css-variabelen, zoals:

/* src/App.vue */
:root {
  --white: '#FFFFFF';
  --black: '#000000';
  --gray: '#EEEEEE';
  --gray-dark: '#5D5D5C';
  --gray-darker: '#414040';
  ...
}

Pictogrammen

Ten eerste raad ik aan om SVG-pictogrammen te gebruiken vanwege hoe configureerbaar ze zijn. U kunt hun grootte wijzigen zonder kwaliteitsverlies, dynamisch hun kleur wijzigen en hun bestandsgrootte is over het algemeen kleiner dan een equivalente png/jpg.

Het verkrijgen van de daadwerkelijke SVG's kan worden gedaan via de exportopties van ontwerptools zoals Figma, Sketch of Illustrator.

Zodra u de bestanden hebt, kunt u ze verder optimaliseren met SVGO, er is een opdrachtregeltool en een webgebaseerde tool.

svg / svgo

⚙️ Node.js-tool voor het optimaliseren van SVG-bestanden

jakearchibald / svgomg

Web-GUI voor SVGO

Beide werken automatisch door ze naar het bestand te verwijzen, hoewel de webversie de beschikbare opties toegankelijker maakt. Zorg ervoor dat u het eindresultaat bekijkt om er zeker van te zijn dat uw pictogrammen er nog steeds goed uitzien.

Vervolgens brengen we de pictogrammen in onze Vue-app, ik heb een methode gebruikt die wordt aanbevolen in het Vue Cookbook. Het is een systeem gemaakt door Sarah Drasner, de SVG-koningin, en je kunt de link ervoor hier vinden.

Om het te laten werken met TailwindCSS, moet je een aantal wijzigingen aanbrengen:

<template functional>
  <svg xmlns="http://www.w3.org/2000/svg"
    :width="props.size"
    :height="props.size"
    viewBox="0 0 16 16"
    :aria-labelledby="props.iconName"
    role="presentation"
    class="fill-current inline-block align-baseline"
    :class="[
      data.class,
      data.staticClass
    ]"
    style="margin-bottom: -2px;"
  >
    <title lang="en">{{ props.icon.replace(/-/g, ' ') }} icon</title>
    <component :is="injections.components[props.iconName]" />
  </svg>
</template>

Omdat SVG's zelf vrij licht zijn, voelde het als een hoop overhead om volledige componenten te gebruiken, dus ik heb wat verdere wijzigingen aangebracht om gebruik te maken van functionele componenten, je kunt mijn vork hier bekijken:

MaxMonteil / vue-sample-svg-icons

Een eigenzinnig voorbeeld van het gebruik van SVG-pictogrammen in een Vue.js-toepassing als functionele componenten

Knoppen

Aanvankelijk wilde ik knoppen op dezelfde manier gebruiken als pictogrammen, met behulp van Vue-componenten, maar dat bleek bedrieglijk ingewikkeld. Het onderdeel moest werken met knoppen, links of een router-link (met vue-router).

Ondersteunende links waren belangrijk voor de toegankelijkheid en semantiek, aangezien links bedoeld zijn om je naar een andere pagina te leiden, terwijl dat niet het geval zou moeten zijn voor knoppen.

Als oplossing heb ik de algemene klassen geëxtraheerd in hun eigen hulpprogramma's in TailwindCSS, wat in pure css gewoon een normale klassenregel is.

Een paar voorbeelden:

@tailwind base;
@tailwind components;

.btn {
    @apply font-medium rounded align-bottom;
}

.btn-primary {
    @apply px-8 py-2;
}

.btn-secondary {
    @apply px-5 py-1;
}

.btn-white {
    @apply text-green-darker bg-white;
}

.btn-green {
    @apply text-white bg-green-dark;
}

@tailwind utilities;

Tekstinvoer

Voor invoer kunnen we Vue-componenten gebruiken, maar er zijn een paar dingen waarmee u rekening moet houden.

Onze wrappercomponenten moeten lichtgewicht en transparant zijn, dat kunnen we doen door functionele componenten te gebruiken en alle attributen en gebeurtenislisteners toe te voegen.

Ik heb ook van de gelegenheid gebruik gemaakt om het label in het onderdeel op te nemen. Het past bij het ontwerp, is toegankelijker en zorgt ervoor dat ik ze nooit vergeet.

Begin met een BaseInput.vue onderdeel:

<!-- src/components/inputs/BaseInput.vue -->
<template functional>
  <label
    :ref="data.ref"
    class="text-sm leading-none font-medium"
    :class="props.makeGray ? 'text-gray-darker' : 'text-green-darker'"
  >
    {{ props.label }}
    <input
      type="text"
      :ref="data.ref"
      class="block mt-2 bg-white rounded w-full outline-none focus:shadow"
      :class="[
        data.class,
        data.staticClass,
      ]"
      :style="[
        data.style,
        data.staticStyle,
      ]"
      v-bind="data.attrs"
      v-on="{ ...listeners, input: e => listeners.input(e.target.value) }"
    >
  </label>
</template>

En hier is een voorbeeld van gebruik van BaseInput.vue :

<!-- src/components/inputs/InputLarge.vue -->

<template functional>
  <component
    :is="injections.components.BaseInput"
    :label="props.label"
    :makeGray="props.makeGray"
    class="font-medium text-3xl text-black pl-4 py-px"
    :class="props.makeGray ? 'bg-gray' : 'bg-white'"
    v-bind="data.attrs"
    v-on="listeners"
  />
</template>

<script>
import BaseInput from '@/components/inputs/BaseInput'

export default {
  name: 'inputLarge',
  inject: {
    components: {
      default: {
        BaseInput
      }
    }
  }
}
</script>

Merk op hoe we de BaseInput.vue . noemen onderdeel. Verrassend genoeg worden geïmporteerde componenten niet weergegeven in functionele componenten bij gebruik van het sjabloonformaat. Dus in plaats daarvan plaatsen we de geïmporteerde componenten in injecties. Ze kunnen ook in rekwisieten worden geplaatst als je dat liever hebt.

Deze methode is ter sprake gebracht in deze github-uitgave:

Functionele component met één bestand met optie voor componenten. #7492

terrierscript gepost op

Versie

2.5.13

Reproductielink

NG-patroon (functioneel)https://codesandbox.io/s/004vv2onw0

OK patroon (niet functioneel)https://codesandbox.io/s/q9k5q8qq56

Stappen om te reproduceren

Ik heb gevonden dat ik components niet kan gebruiken optie wanneer functional enkel bestand component.

<template functional>
  <div>
    <some-children />
  </div>
</template>

<script>
import SomeChildren from "./SomeChildren"

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

Het is gebeurd Unknown custom element .

Wat wordt er verwacht?

Niet voorkomen Unknown custom element en gebruik onderliggende component

Wat gebeurt er eigenlijk?

Het is gebeurd Unknown custom element

Als tijdelijke oplossing komt het niet voor bij gebruik van Vue.component .

import Vue from "vue"
import SomeChildren from "./SomeChildren"
Vue.component("some-children", SomeChildren);

export default {}

// can use  <some-children />
Weergeven op GitHub

Keuzerondjes

Na alle elementen die we tot nu toe hebben gedaan, zijn Radio Buttons niet veel anders. Het verschil is dat het stylen ervan meer betrokken kan zijn. Hoewel het mogelijk is om in plaats daarvan standaardknoppen te gebruiken, wilde ik de standaard keuzerondjes gebruiken, opnieuw voor semantiek en toegankelijkheid.

De truc die ik ontdekte was om het gedrag van labels die radioknoppen omhulden te gebruiken.
De knoppen zelf zijn klein en moeilijk aan te raken/aan te klikken, maar als u ze in een label wikkelt, wordt door ergens op het labelvakje te klikken ook het keuzerondje geselecteerd.
Hiermee heb ik keuzerondjes gestileerd door het label er echt uit te laten zien zoals ik wilde en de keuzerondjes in het label te verbergen.

<template functional>
  <div
    :ref="data.ref"
    :class="[
      data.class,
      data.staticClass,
    ]"
    :style="[
      data.style,
      data.staticStyle,
    ]"
  >
    <label
      v-for="label in props.labels"
      :key="label.value || label"
      class="relative px-3 py-1 rounded"
      :class="(label.value || label) === props.modelValue ? '...' : '...'"
    >
      <slot :label="label">
        {{ label.text || label }}
      </slot>
      <input
        :ref="data.ref"
        type="radio"
        class="sr-only"
        :value="label.value || label"
        v-on="{ ...listeners, input: e => listeners.input(e.target.value) }"
        :checked="(label.value || label) === props.modelValue"
      >
    </label>
  </div>
</template>

Wees voorzichtig bij het verbergen van het keuzerondje omdat het nog steeds zichtbaar moet zijn voor schermlezers, tailwind biedt hiervoor een klasse aan, in standaard css die er als volgt uitziet:

.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;
}

Wat betreft de v-slot en alle OK's (||), die leg ik uit in het volgende gedeelte over select ingangen.

Een echt nuttige bron die ook over selectievakjes gaat:

Smashing Magazine - Aangepaste invoer maken in VueJS

Selecteer ingang

Het geselecteerde onderdeel is leuk om in te pakken, zowel qua ontwerp als functionaliteit.

Qua ontwerp was het verrassend om te ontdekken hoe "hacky" het is om de standaard neerwaartse pijl te veranderen. Er zijn een paar manieren om dit te doen, maar de truc die ik gebruikte, is om de standaardstijl te verwijderen door appearance: none; in te stellen en vervolgens mijn SVG naar keuze binnen te halen met de URL-functie van CSS.

Om iets soortgelijks te doen, moet je je SVG-tag coderen in een URL-compatibele string, ik vond deze site om precies dat te doen:

URL-encoder voor SVG

Dan zijn er nog enkele positionerings- en spatiëringsstijlen om het pictogram te plaatsen waar u maar wilt.

Voor functionaliteit moet de eindgebruiker de controle behouden over hoe de dropdown-waarden worden weergegeven, de oplossing is om scoped slots te gebruiken. Met deze methode kan onze component elke reeks waarden ondersteunen.

Dit komt omdat het officiële Vue-document voorbeelden toont met behulp van een String Array en een Object Array om de select te vullen.

<template functional>
  <label
    class="text-sm font-medium text-green-darker"
  >
    {{ props.label }}
    <select
      :ref="data.ref"
      class="custom-arrow bg-no-repeat block mt-2 pl-2 pr-6 bg-white rounded text-black text-lg outline-none focus:shadow"
      :class="[
        data.class,
        data.staticClass,
      ]"
      :style="[
        data.style,
        data.staticStyle,
      ]"
      v-bind="data.attrs"
      v-on="{ ...listeners, input: e => listeners.input(e.target.value) }"
    >
      <option disabled value="">-</option>
      <option
        v-for="option in props.values"
        :value="option.value || option"
        :key="option.value || option"
      >
        <slot :option="option" />
      </option>
    </select>
  </label>
</template>

<script>
export default {
  name: 'inputSelect',
  props: {
    label: {
      type: String,
      require: true
    },
    values: {
      type: [Array, Object],
      require: true
    }
  }
}
</script>

<style scoped>
.custom-arrow {
  -moz-appearance: none;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10'%3E%3Cpath fill='%23000000' d='M7.443 9.057L5.229 6.843a.666.666 0 01.943-.942l2.214 2.214 2.199-2.199a.666.666 0 11.942.942l-3.142 3.143-.942-.944z'/%3E%3C/svg%3E");
  background-origin: border-box;
  background-position: top 35% right 0.5rem;
}
</style>

Deze wrappercomponenten bieden ook de mogelijkheid om best practices af te dwingen, zoals een label voor een invoer en een lege uitgeschakelde eerste optie voor betere iOS-ondersteuning.

Conclusie

Nou, je hebt het einde bereikt, dit omvat alle componenten die ik heb meegebracht uit mijn ontwerpbibliotheek. Het is eigenlijk maar het oppervlak van wat mogelijk is en ik kon niet hopen volledig uitputtend te zijn. Desalniettemin hoop ik dat dit je heeft geholpen en je heeft aangemoedigd om dat herontwerp aan te pakken of zelfs maar te beginnen met ontwerpen!

De volgende stap na het inpakken van al deze invoervelden zou zijn om ze samen te stellen in een formulier of andere lay-out voor hergebruik. Dit zijn enkele van de componenten die u in de src/layouts zou plaatsen map. Je kunt een vervolgartikel verwachten als ik het zelf af heb :D

Voel je vrij om hieronder opmerkingen achter te laten en als je vragen hebt of een follow-up wilt, kun je me vinden op twitter @MaxMonteil

  • Markus O. - Tailwind CSS instellen met Vue.js
  • Markus O. - Herbruikbare functionele Vue.js-componenten met Tailwind CSS