Using :global() and custom data attributes to apply styles to dynamically added classes

Currently, I am working on creating a typing game that is reminiscent of monkeytype.com. In this game, every letter is linked to classes that change dynamically from an empty string to either 'correct' or 'incorrect', depending on whether the user's input matches the expected letter.

One issue I'm facing is related to how Svelte handles unused classes - the 'correct' and 'incorrect' classes may not exist in the DOM right away. This makes it challenging for me to apply specific styles to these letter classes when they become active.

Below is a snippet of my code:

<script lang="ts">
    type Game = 'waiting for game' | 'in progress' | 'game over';
    type Word = string;

    let game: Game = 'waiting for game';
    let typedLetter = '';

    let words: Word[] = 'the quick brown fox jumped over the lazy dog'.split(' ');

    let wordIndex = 0;
    let letterIndex = 0;
    let correctLetter = 0;

    let correctLetters = 0;

    let wordsEl: HTMLDivElement;
    let letterEl: HTMLSpanElement;
    let inputEl: HTMLInputElement;

    function startGame() {
        setGameState('in progress');
    }

    function setGameState(state: Game) {
        game = state;
    }

    function updateGameState() {
        setLetter();
        checkLetter();
        nextLetter();
        resetLetter();
    }

    function setLetter() {
        const isWordCompleted = letterIndex > words[wordIndex].length - 1;

        if (!isWordCompleted) {
            letterEl = wordsEl.children[wordIndex].children[letterIndex] as HTMLSpanElement;
        }
    }

    function checkLetter() {
        const currentLetter = words[wordIndex][letterIndex];

        if (typedLetter === currentLetter) {
            letterEl.dataset.letter = 'correct';
            increaseScore();
        }

        if (typedLetter !== currentLetter) {
            letterEl.dataset.letter = 'incorrect';
        }
    }

    function increaseScore() {
        correctLetters += 1;
    }

    function nextLetter() {
        letterIndex += 1;
    }

    function resetLetter() {
        typedLetter = '';
    }

    function handleKeydown(event: KeyboardEvent) {
        if (event.code === 'Space') {
            event.preventDefault();
        }

        if (game === 'waiting for game') {
            startGame();
        }
    }
</script>

<div class="game" data-game={game}>
    <input
        type="text"
        class="input"
        bind:this={inputEl}
        bind:value={typedLetter}
        on:input={updateGameState}
        on:keydown={handleKeydown}
    />
</div>

<div class="words" bind:this={wordsEl}>
    {#each words as word}
        <span class="word">
            {#each word as letter}
                <span class="letter">{letter}</span>
            {/each}
        </span>
    {/each}
</div>

<style lang="scss">
    .words {
        --line-height: 1em;
        --lines: 3;

        width: 100%;
        max-height: calc(var(--line-height) * var(--lines) * 1.42);
        display: flex;
        flex-wrap: wrap;
        gap: 0.6em;
        position: relative;
        font-size: 1.5rem;
        line-height: var(--line-height);
        overflow: hidden;
        user-select: none;

        .letter {
            opacity: 0.4;
            transition: all 0.3s ease;

            &:global([data-letter='correct']) {
                opacity: 0.8;
            }

            &:global([data-letter='incorrect']) {
                color: var(--primary);
                opacity: 1;
            }
        }
    }
</style>

I've made an attempt to use :global for the data attributes, but I encountered an error message stating ':global(...) not at the start of a selector sequence should not contain type or universal selectors svelte(css-invalid-global-selector-position)'. All packages and Svelte are fully up to date.

Answer №1

If the component's markup creates the element, there is no need for :global.

Typically, using :global should not be required as Svelte generates scoped classes for elements.

You can also specify attributes and classes directly in the markup without needing to manipulate them through code.

For instance:

<script>
    let correct = 'world';
    let words = ['loner'];
</script>

<div class="words">
    {#each words as word}
        <span class="word">
            {#each word as letter, i}
                <span class="letter"
                    class:exists={correct.indexOf(letter) != -1}
                    class:correct={correct[i] == letter}>
                    {letter}
                </span>
            {/each}
        </span>
    {/each}
</div>

<style>
    .letter.exists { color: orange; }
    .letter.correct { color: green; }
</style>

REPL

Using a data-attribute:

<span class="letter"
    data-letter={
        correct[i] == letter ? 'correct' :
        correct.indexOf(letter) != -1 ? 'exists' :
        undefined
    }>
...
<style>
    .letter[data-letter=exists] { color: orange; }
    .letter[data-letter=correct] { color: green; }
</style>

REPL

Answer №2

When you see the error message:

:global(...) not at the start of a selector sequence should not contain type or universal selectors

it indicates that the compiled style is invalid CSS. For more information, visit this Github issue and try the solutions in the Svelte playground

To properly target the <span/> elements with the data-letter attribute, make sure to nest the selector inside the :global selector like below:

.word > :global(.letter[data-letter="correct"]) {
  opacity: 0.8;
}

.word > :global(.letter[data-letter="incorrect"]) {
  color: var(--primary);
  opacity: 1;
}

Ensure that the .word > :global(...) selectors are placed outside of the .words selector.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible for Next.js to retrieve the window size without resorting to a faulty hook call or encountering an undefined window

In my ongoing efforts to dynamically adjust the size of an image within a next.js application to make it responsive to various screen sizes, I have encountered challenges. The different methods I have attempted and observed have resulted in either an inv ...

Transitioning smoothly between different backgrounds will be like changing the cursor color

As I work on my website, I encounter the need for a cursor that smoothly changes its color. Specifically, I want the cursor to appear blue (#0059ff) when hovering over a white background and white when over a blue background, with a seamless transition lik ...

Attempting to intercept a 401 error in an HTTP interceptor, I aim to refresh my session and then retry the initial request

My goal is to intercept responses returning from the /api, catching them if they are a 401 error, executing a refresh session action, and then retrying the original HTTP call again (while also preventing it from infinitely looping if another 401 error occu ...

Using import statement is mandatory when loading ES Module in TS Node: server/src/index.ts

Attempting to kickstart a TypeScript Node project, I've gone ahead and added some essential dependencies (TypeScript, ESLint, Mongoose, and GraphQL). However, upon executing the command: ts-node-dev --respawn --transpile-only src/index.ts An error me ...

Enhance Summernote functionality by creating a custom button that can access and utilize

Using summernote in my Angular project, I am looking to create a custom button that can pass a list as a parameter. I want to have something like 'testBtn': this.customButton(context, listHit) in my custom button function, but I am unsure how to ...

Troubleshooting Angular 2 Typescript: Component not displaying as expected

I am currently in the process of learning Angular 2. Despite encountering numerous error messages, I have successfully set up the routes and can now display the desired component. My next challenge is to incorporate a navbar component into my homepage comp ...

Is there a way to retrieve the chosen value from an ion-alert radio alert?

async showAlertRadio(heading:string){ const alert = await this.alertCtrl.create({ header: heading, inputs :[ { name : 'Radio 1', type: 'radio', label: 'Radio 1', ...

"Adjusting the position of an Ionic Menu on-the-fly

As I strive to update the Ionic 3 Menu side dynamically when the user changes the language, a challenge arises for RTL languages where the menu needs to be on the right instead of the default left. To tackle this issue, I have subscribed to the TranslateS ...

The type 'Dispatch<SetStateAction<boolean>>' cannot be assigned to type 'boolean'

Currently, I am attempting to transfer a boolean value received from an onChange function to a state variable. let [toggleCheck, setToggleCheck] =useState(false);` <input type="checkbox" id={"layout_toggle"} defaultChecked={toggleCh ...

Angular update row and save data to an array

When comparing the data of an edited row with the row just below it, I encounter a specific scenario. In a table containing 5 rows, as I edit records from top to bottom using the provided code snippet, I am able to store the values in an array. The most re ...

Recursive types in TypeScript allow for the definition of types that

Is there a way to implement the function below without utilizing any? Playground type MyType = { name: string, age: number, score: { prime: number, }, prize: { first: { discount: number } } } export const trim = ( myObj: ...

issue with visibility of Angular Component

After following a straightforward YouTube tutorial for beginners on Angular, I encountered an issue. The tutorial covers how to use components, and even though I understand the concept well, the component simply does not appear no matter what I try. Here i ...

Tips for Achieving Observable Synchronization

I've encountered a coding challenge that has led me to this code snippet: ngOnInit(): void { this.categories = this.categoryService.getCategories(); var example = this.categories.flatMap((categor) => categor.map((categories) = ...

Tips for resolving type checking errors in TypeScript and React

I encountered an error and would appreciate any assistance in resolving it. My tech stack consists of react, typescript, and Material UI. I am attempting to customize a button using the following link: https://mui.com/material-ui/customization/how-to-custo ...

The Angular router is causing an issue where when navigating back, my component does not reset to 0 as expected, resulting in

I'm currently working on an ionic-angular app and implementing a Register feature where users input their information step by step. The issue I'm facing is with the backward navigation functionality - when users go back using the arrow button, th ...

Error message in React NodeJs stack: "The property ' ' does not exist on type 'never' in Typescript."

Recently, I have been immersing myself in learning NodeJs, Express, React, monogoDB and Typescript after working extensively with MVC C# SQL Databases. For my first project, I created a simple Hello World program that interacts with an Express server to d ...

Comparing strings with data in objects using Angular

all. I have a query. What is the optimal method for comparing data? For instance, if you have a constant response = 225235743; And I aim to locate and exhibit all data in an object with the same ID as the one in this response. This needs to be resolved ...

Configuring the parameters property for a SSM Association in AWS CDK

I am working on utilizing the AWS Systems Manager State Manager to automate shutting down an RDS instance at 9PM using a cron job. Currently, I am constructing the CloudFormation template with the help of AWS CDK. While going through the AWS CDK documenta ...

Using Typescript: ForOf Iteration with Unknown Value Types

My journey began with a quick peek at this particular inquiry. However, the approach discussed there utilized custom typing. I am currently iterating over object entries using a for-of loop. Here's a snippet of the values I'm dealing with below. ...

Is SASS stylesheet supported by the Angular 2 Ahead-of-Time compiler?

Considering giving Angular 2 Ahead-of-Time compilation another shot. I'll have to do some significant refactoring since my current custom build process will need to be reworked. Prior to diving in, I'm curious: if I reference external .scss fil ...