Components · Data Entry
Select
Pick one value (or several) from a known set — with type-to-search — rendered through the shared Select.
Examples
<!-- Paste-and-run: save as .html and open in a browser. No build step, no bundler.
Select is a COMPOSITE (antd, no framework-free element), so its HTML form is a
CDN-React page — React + antd load from a CDN (esm.sh) and mount on open. It
consumes the SAME shared `selectTheme` the React/Vue wrappers use, so it renders
the DS V3 look (32px control, radius 8, #E3E3E3 border, brand-tint selected option).
No JSX here (JSX needs a compiler) — we use React.createElement via the `h` alias. -->
<div id="root"></div>
<script type="module">
import React from 'https://esm.sh/react@18';
import { createRoot } from 'https://esm.sh/react-dom@18/client';
import { ConfigProvider, Select } from 'https://esm.sh/antd@6?deps=react@18,react-dom@18';
import { selectTheme } from 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/select-theme.js';
const h = React.createElement;
const options = [
{ value: 'poll', label: 'Poll' },
{ value: 'quiz', label: 'Quiz' },
{ value: 'wordcloud', label: 'Word cloud' },
{ value: 'qa', label: 'Q&A' },
];
// Same shared theme the React/Vue wrappers pass to ConfigProvider.
// The DS V3 matrix is just props: size ('small'|'default'|'large'),
// status ('error'|'warning'), mode ('multiple'|'tags'), allowClear, loading, disabled.
createRoot(document.getElementById('root')).render(
h(ConfigProvider, { theme: selectTheme },
h(Select, {
showSearch: true,
size: 'default', // 'small' | 'default' | 'large' → 24 / 32 / 40
mode: 'multiple', // omit for single; 'multiple' | 'tags'
allowClear: true,
placeholder: 'Choose a slide type',
options,
style: { width: 240 },
})
)
);
</script>
import { ConfigProvider, Select } from 'antd'; // antd v6
import { selectTheme } from '@ahaslides-product/design/select-theme';
const options = [
{ value: 'poll', label: 'Poll' },
{ value: 'quiz', label: 'Quiz' },
{ value: 'wordcloud', label: 'Word cloud' },
{ value: 'qa', label: 'Q&A' },
];
<ConfigProvider theme={selectTheme}>
<Select
showSearch
size="default" // small | default | large → 24 / 32 / 40
status={error ? 'error' : undefined} // 'error' | 'warning' for validation
mode="multiple" // omit for single; 'multiple' | 'tags'
allowClear // × to clear the selection
loading={fetching} // spinner while async options load
disabled={locked}
placeholder="Choose a slide type"
options={options}
value={type}
onChange={setType}
style={{ width: 240 }}
/>
</ConfigProvider>
// One shared selectTheme → the DS V3 look; never restyle a bare <Select> per call site.
<script setup>
import { ref } from 'vue';
import { ConfigProvider, Select } from 'ant-design-vue'; // ant-design-vue v4
import { selectTheme } from '@ahaslides-product/design/select-theme';
const type = ref('poll');
const options = [
{ value: 'poll', label: 'Poll' },
{ value: 'quiz', label: 'Quiz' },
{ value: 'wordcloud', label: 'Word cloud' },
{ value: 'qa', label: 'Q&A' },
];
</script>
<template>
<a-config-provider :theme="selectTheme">
<a-select
v-model:value="type"
show-search
size="default" <!-- small | large → 24 / 40 -->
:status="error ? 'error' : ''" <!-- 'error' | 'warning' -->
mode="multiple" <!-- omit for single; 'multiple' | 'tags' -->
allow-clear
:loading="fetching"
:disabled="locked"
placeholder="Choose a slide type"
:options="options"
style="width: 240px"
/>
</a-config-provider>
</template>
<!-- The SAME selectTheme as React → one DS V3 look across both vendor libraries. -->
// @ahaslides-product/design/select-theme — declared ONCE, consumed by both tiers.
import { selectTheme } from '@ahaslides-product/design/select-theme';
export const selectTheme = {
token: {
colorPrimary: '#6A1EBB',
borderRadius: 8,
controlHeight: 32,
colorBorder: '#E3E3E3', // 1px rest border
colorTextPlaceholder: '#8A8A8A',
fontFamily: 'var(--aha-font-product, "Plus Jakarta Sans", sans-serif)',
},
components: {
Select: {
optionSelectedBg: '#F9F5FF', // brand-tint selected row
optionSelectedColor: '#6A1EBB',
optionActiveBg: '#F7F7F7', // keyboard/hover row
borderRadius: 8,
controlHeight: 32,
},
},
};
API
| Prop | Type | Default | Notes |
|---|---|---|---|
options | { label, value }[] | [] | The selectable set |
value | string | string[] | — | Selected value(s); array when mode=multiple |
showSearch | boolean | true | Type to filter options |
mode | single | multiple | tags | single | Selection cardinality |
size | small | default | large | default | Control height 24 / 32 / 40 |
status | error | warning | — | Validation border colour |
allowClear | boolean | false | Show an × to clear the selection |
disabled | boolean | false | Non-interactive, muted control |
loading | boolean | false | Spinner while async options load |
placeholder | string | — | Hint shown when empty |
onChange | (value) => void | — | Fires with the new selection |
Install
# .npmrc — once: point the @ahaslides-product scope at GitHub Packages
@ahaslides-product:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} # a GitHub token with read:packages
npm i @ahaslides-product/design
import '@ahaslides-product/design/tokens.css'; // once, at the app root
import '@ahaslides-product/design/select-theme'; // registers <select>
Agent feed for this component (absolute, fetchable anywhere): select.agent.json · select.md · select.llms.txt
When to use
When to use
- Select — one value from a known set of more than ~5 options — don't make the user type it
- Radio — 2–5 mutually-exclusive options all worth showing at once
- Input — free text that isn't a known set
Pre-select the recommended default rather than leaving it empty. Keep option labels short and sentence case; for long lists keep showSearch on. For a short, known set (or a select inside a shadow-DOM surface like a settings row) reach for the leaf Select field (slug `select-field`, <aha-select>) instead — this composite is for search / tags / multi / virtualised lists.
Surfaces
editor dashboard settings
Spec
Control height 32 · radius 8 · 1px #E3E3E3 border · Search showSearch — type to filter options · Focus brand #6A1EBB border · Selected option brand-tint #F9F5FF row, #6A1EBB label · Placeholder #8A8A8A · Size small 24 · default 32 · large 40 · Status error #F5222D · warning #FAAD14 border