Skip to content

feat: add support for space separated cssClasses in settings#664

Merged
brianvoe merged 3 commits intobrianvoe:masterfrom
kilianc:master
Dec 11, 2025
Merged

feat: add support for space separated cssClasses in settings#664
brianvoe merged 3 commits intobrianvoe:masterfrom
kilianc:master

Conversation

@kilianc
Copy link
Contributor

@kilianc kilianc commented Dec 10, 2025

This makes it easier to use tailwind or simply add more classes instead of simply override them, for example

new SlimSelect({
  select: document.getElementById('slim-select'),
  cssClasses: {
    main: 'ss-main my-class',
...

@kilianc
Copy link
Contributor Author

kilianc commented Dec 10, 2025

Not as easy as I thought

render.ts:953 Uncaught SyntaxError: Failed to execute 'querySelectorAll' on 'Element': '.ss-option !text-xs !py-1 !px-2:not(.ss-placeholder text-xs):not(.ss-disabled):not(.ss-hide)' is not a valid selector.
    at N.getOptions (render.ts:953:41)
    at N.highlight (render.ts:959:26)
    at t.onkeydown (render.ts:790:40)

Maybe the right change is to have additionalCssClasses?

@brianvoe
Copy link
Owner

I agree with the premise. The way I would tackle it is if someone sets a field on cssClasses then you would add it to the existing string for example "ss-main my-class tailwind-sucks" then when we need to add/remove we split on string and loop through add and loop through remove.

If you dont want to tackle it throw me a donation and ill get it knocked out.

@kilianc
Copy link
Contributor Author

kilianc commented Dec 10, 2025

I was going to donate anyways, you're on the nice list :) – but I want to do it myself and feel helpful if I can.

The way I would tackle it is if someone sets a field on cssClasses then you would add it to the existing string for example "ss-main my-class tailwind-sucks" then when we need to add/remove we split on string and loop through add and loop through remove.

Sure, this basically always keeps ss-main attached to the element, and btw, it does have drawbacks because sometimes you don't want that, but it's a implementation decision like any other.

My error is generating from the fact that somehow classes are used for both interactivity and styling which makes it hard to reason about this, at least for me. That error was generated by pressing arrow down for example.

I wonder if we need to always keep the default classes, and only use the first class for the interactions? Or we separate styling and interactivity so that this is not an issue?

WYT?

@brianvoe
Copy link
Owner

Thanks for the kind words!

Sure, this basically always keeps ss-main attached to the element, and btw, it does have drawbacks because sometimes you don't want that, but it's a implementation decision like any other.

I get your thought process but if we remove the primary initial class then you would have to write all the css for everything else underneath it. At that point you might as well just copy the full css and just make your own changes and use that.

My error is generating from the fact that somehow classes are used for both interactivity and styling which makes it hard to reason about this, at least for me. That error was generated by pressing arrow down for example.

If we keep the primary initial class then you shouldnt get the error anymore.

In future releases that would probably be breaking change major release changes i can update interactivity to be not reliant on css classes.

@kilianc
Copy link
Contributor Author

kilianc commented Dec 11, 2025

Ok let me give it a shot at:

  • classes added to cssClasses.<opt> are appended
  • interactivity doesn't break by only using the default classes
    • :not(.ss-placeholder text-xs) vs :not(.ss-placeholder)

@kilianc
Copy link
Contributor Author

kilianc commented Dec 11, 2025

There is a positioning regression introduced after a3f5f65 that makes me uncomfortable pushing new code. I'll wait until that fixed.

See repro here where position.left is off on all select from main.

Details
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>SlimSelect - Custom Classes Test</title>

    <!-- Local build (testing new changes) -->
    <!-- <link rel="stylesheet" href="dist/slimselect.css" /> -->

    <!-- CDN version (for regression testing - uncomment to use) -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/slim-select@latest/dist/slimselect.css" />

    <style>
      body {
        font-family: Arial, sans-serif;
        max-width: 800px;
        margin: 50px auto;
        padding: 20px;
      }
      .test-section {
        margin: 30px 0;
        padding: 20px;
        border: 2px solid #ddd;
        border-radius: 8px;
      }
      h1 {
        color: #333;
      }
      h2 {
        color: #666;
        font-size: 1.2em;
        margin-bottom: 10px;
      }
      .description {
        margin-bottom: 15px;
        color: #666;
        font-size: 0.95em;
      }
      /* Custom classes for testing */
      .text-xs {
        font-size: 0.75rem;
      }
      .italic {
        font-style: italic;
      }
      .custom-option {
        font-weight: bold;
        color: #5897fb;
      }
      .info-box {
        background: #f0f8ff;
        border-left: 4px solid #5897fb;
        padding: 10px;
        margin: 20px 0;
      }
      .success {
        color: #00b755;
        font-weight: bold;
      }
      code {
        background: #f4f4f4;
        padding: 2px 6px;
        border-radius: 3px;
        font-family: 'Courier New', monospace;
      }
    </style>
  </head>
  <body>
    <h1>SlimSelect Custom Classes Test</h1>

    <div class="info-box">
      <strong>✓ Fixed:</strong> Custom classes are now <strong>appended</strong> to default classes instead of replacing
      them. This ensures internal functionality (like <code>:not(.ss-placeholder)</code> selectors) continues to work
      correctly.
    </div>

    <div class="test-section">
      <h2>Test 1: Default Behavior (No Custom Classes)</h2>
      <div class="description">
        This select uses only default SlimSelect classes. Placeholder should have class: <code>ss-placeholder</code>
      </div>
      <select id="test1">
        <option data-placeholder="true">Select an option...</option>
        <option value="1">Option 1</option>
        <option value="2">Option 2</option>
        <option value="3">Option 3</option>
      </select>
    </div>

    <div class="test-section">
      <h2>Test 2: Custom Placeholder Classes</h2>
      <div class="description">
        Using <code>cssClasses: { placeholder: 'text-xs italic' }</code><br />
        Placeholder should have classes: <code>ss-placeholder text-xs italic</code>
      </div>
      <select id="test2">
        <option data-placeholder="true">This placeholder is smaller and italic</option>
        <option value="1">Option 1</option>
        <option value="2">Option 2</option>
        <option value="3">Option 3</option>
      </select>
    </div>

    <div class="test-section">
      <h2>Test 3: Custom Option Classes</h2>
      <div class="description">
        Using <code>cssClasses: { option: 'custom-option' }</code><br />
        Options should have classes: <code>ss-option custom-option</code>
      </div>
      <select id="test3">
        <option data-placeholder="true">Select an option...</option>
        <option value="1">Bold Blue Option 1</option>
        <option value="2">Bold Blue Option 2</option>
        <option value="3">Bold Blue Option 3</option>
      </select>
    </div>

    <div class="test-section">
      <h2>Test 4: Multiple Custom Classes</h2>
      <div class="description">
        Using multiple custom classes at once<br />
        Demonstrates that you can add multiple space-separated classes
      </div>
      <select id="test4">
        <option data-placeholder="true">Small italic placeholder...</option>
        <option value="1">Bold Blue Option 1</option>
        <option value="2">Bold Blue Option 2</option>
        <option value="3">Bold Blue Option 3</option>
      </select>
    </div>

    <div class="info-box">
      <strong>Testing Instructions:</strong>
      <ol>
        <li>Open your browser's DevTools (F12)</li>
        <li>Click on each select to open the dropdown</li>
        <li>Inspect the placeholder and option elements</li>
        <li>Verify that <strong>both</strong> default (ss-*) and custom classes are present</li>
        <li>Verify that keyboard navigation and selection still work correctly</li>
      </ol>
    </div>

    <!-- Local build (testing new changes) -->
    <script src="dist/slimselect.js"></script>

    <!-- CDN version (for regression testing - uncomment to use) -->
    <!-- <script src="https://cdn.jsdelivr.net/npm/slim-select@latest/dist/slimselect.js"></script> -->

    <script>
      // Test 1: Default behavior
      new SlimSelect({
        select: '#test1'
      })

      // Test 2: Custom placeholder classes
      new SlimSelect({
        select: '#test2',
        cssClasses: {
          main: 'my-class',
          placeholder: 'my-class',
          values: 'my-class',
          single: 'my-class',
          max: 'my-class',
          value: 'my-class',
          valueText: 'my-class',
          valueDelete: 'my-class',
          valueOut: 'my-class',
          deselect: 'my-class',
          arrow: 'my-class',
          content: 'my-class',
          contentOpen: 'my-class',
          dirAbove: 'my-class',
          dirBelow: 'my-class',
          search: 'my-class',
          searchHighlighter: 'my-class',
          searching: 'my-class',
          addable: 'my-class',
          list: 'my-class',
          optgroup: 'my-class',
          optgroupLabel: 'my-class',
          optgroupLabelText: 'my-class',
          optgroupActions: 'my-class',
          optgroupSelectAll: 'my-class',
          optgroupClosable: 'my-class',
          option: '!text-xs !py-1 !px-2',
          highlighted: 'my-class',
          mainOpen: 'my-class',
          close: 'my-class',
          selected: 'my-class',
          error: 'my-class',
          disabled: 'my-class',
          hide: 'my-class'
        }
      })

      // Test 3: Custom option classes
      new SlimSelect({
        select: '#test3',
        cssClasses: {
          option: 'custom-option'
        }
      })

      // Test 4: Multiple custom classes
      new SlimSelect({
        select: '#test4',
        cssClasses: {
          placeholder: 'text-xs italic',
          option: 'custom-option'
        }
      })

      // Log the classes for debugging
      setTimeout(() => {
        console.log('=== Test Results ===')

        // Test 2 - Check placeholder classes
        const test2Main = document.querySelector('#test1').nextElementSibling
        const test2Placeholder = test2Main?.querySelector('.ss-placeholder')
        console.log('Test 1 placeholder classes:', test2Placeholder?.className)

        const test3Main = document.querySelector('#test2').nextElementSibling
        const test3Placeholder = test3Main?.querySelector('.ss-placeholder')
        console.log('Test 2 placeholder classes:', test3Placeholder?.className)

        // Test 3 - Check option classes (need to open dropdown first)
        console.log('\n✓ Open each dropdown to see option classes in the DOM')
        console.log('✓ All classes should include both ss-* defaults and custom classes')
      }, 100)
    </script>
  </body>
</html>

@kilianc
Copy link
Contributor Author

kilianc commented Dec 11, 2025

Ok I updated the PR. WYT?

@brianvoe
Copy link
Owner

I think everything looks good to me. Nice Job! If your ready ill pull it in and double check a few things a get a release out.

Also thanks for the donation. I really appreciate it! It will be nice date night money or the wife and I.

@kilianc
Copy link
Contributor Author

kilianc commented Dec 11, 2025

Go for it @brianvoe! I appreciate you letting me contribute.

@brianvoe brianvoe merged commit 67e31ba into brianvoe:master Dec 11, 2025
1 check passed
@brianvoe
Copy link
Owner

Published v3.3.0

Nice Job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants