4
\$\begingroup\$

The objective of the game is to guess the hue, saturation, and lightness values of a given color swatch across ten rounds.

This being my first svelte project, I'd like some insight into best practices, especially svelte-specific ones.

Versions

  • svelte: ^3.0.0

Specific questions

  1. Are my choices of abstraction adequate? Are there things that should or shouldn't be components of their own?
  2. In src/components/swatch.svelte, is it okay to style just div (as opposed to adding a class .swatch to the div and styling that), given that svelte scopes styles to the file?
  3. In src/components/results.svelte, I'm a bit concerned about how complicated the score calculation line looks. Is there a way to clean that up a little bit? To my best knowledge I can't assign variables in svelte's template syntax.
  4. Is it standard/acceptable to use an array of length 3 to represent an HSL colour or should I switch to an object with three keys, say h, s, and l?
  5. I was considering using a @svelte/store to store the solution and input history, but eventually decided against it because I thought it was overkill for this. When would one use @svelte/store?

I'm using prettier for linting, so please avoid feedback on code formatting.

Any other feedback is also welcome.

File structure

.
├── package-lock.json
├── package.json
├── public
│   ├── build
│   │   └── …
│   ├── favicon.png
│   ├── global.css
│   └── index.html
├── readme.md
├── rollup.config.js
└── src
    ├── app.svelte
    ├── components
    │   ├── results.svelte
    │   ├── slider.svelte
    │   └── swatch.svelte
    └── main.js

Code

I have replaced the contents of <style> tags with for brevity.

src/main.js

import App from "./app.svelte";

const app = new App({
    target: document.body,
    props: {}
});

export default app;

src/app.svelte

<script>
    import Swatch from "./components/swatch.svelte";
    import Slider from "./components/slider.svelte";
    import Results from "./components/results.svelte";
    import random from "random-int";

    import "../node_modules/milligram/dist/milligram.min.css";

    let solution = randomColor();
    let input = [0, 0, 0];
    let history = [];

    const totalRounds = 10;
    let round = 1;

    function save() {
        const solutionCopy = [...solution];
        const inputCopy = [...input];

        history.push({
            solution: solutionCopy,
            input: inputCopy
        });

        round++;

        solution = randomColor();
    }

    function randomColor() {
        return [random(0, 255), random(0, 100), random(0, 100)];
    }
</script>

<style>
    …
</style>

<h1>Guess the HSL</h1>

{#if round <= totalRounds}
    <label for="progress">Round {round} / {totalRounds}</label>
    <progress id="progress" value={round} max={totalRounds} />

    <div class="main">
        <Swatch color={solution} inline={false} />

        <Slider name="Hue" bind:value={input[0]} range={[0, 360]} step={1} />
        <Slider
            name="Saturation"
            bind:value={input[1]}
            range={[0, 100]}
            step={1} />
        <Slider
            name="Lightness"
            bind:value={input[2]}
            range={[0, 100]}
            step={1} />
    </div>

    <input type="submit" on:click={save} value="Submit" />
{:else}
    <Results {history} />
{/if}

src/components/swatch.svelte

<script>
    export let width = 50;
    export let color = [0, 0, 0];
    export let inline = true;
</script>

<style>
    …
</style>

<div
    style=" width: {width}px; height: {width}px; background: hsl({color[0]}, {color[1]}%,
    {color[2]}%);"
    class={inline ? '' : 'display'} />

src/components/slider.svelte

<script>
    export let name;
    export let value;
    export let range = [0, 100];
    export let step = 1;
</script>

<style>
    …
</style>

<fieldset>
    <legend>{name}</legend>

    <input
        type="range"
        {name}
        bind:value
        min={range[0]}
        max={range[1]}
        {step} />
    <input
        type="number"
        name="hue"
        bind:value
        min={range[0]}
        max={range[1]}
        {step} />
</fieldset>

src/components/results.svelte

<script>
    import Swatch from "./swatch.svelte";
    import round from "round-to";
    import convert from "color-convert";
    import { differenceCiede2000 as ciede2000 } from "d3-color-difference";

    export let history = [];
</script>

<style>
    …
</style>

<table>
    <thead>
        <th>Solution</th>
        <th>Input</th>
        <th>Score</th>
    </thead>

    {#each history as { solution, input }}
        <tr>
            <td>
                <Swatch color={solution} width="25" />
                <code>hsl({solution[0]}, {solution[1]}%, {solution[2]}%)</code>
            </td>

            <td>
                <Swatch color={input} width="25" />
                <code>hsl({input[0]}, {input[1]}%, {input[2]}%)</code>
            </td>

            <td>
                {round(100 - ciede2000(`#${convert.hsl.hex(solution)}`, `#${convert.hsl.hex(input)}`), 2)}
            </td>
        </tr>
    {/each}
</table>
```
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.