diff --git a/addons/rose/addon/components/rose/checkbox.js b/addons/rose/addon/components/rose/checkbox.js
deleted file mode 100644
index 498f01042..000000000
--- a/addons/rose/addon/components/rose/checkbox.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import Component from '@ember/component';
-import layout from '../../templates/components/rose/checkbox';
-
-/**
- A simple ember input `` element with associated `label`.
-
- @example
-
- Label
- Name
- Form
- Value
- Error
- Checked
- Disabled
- Disabled + Checked
-**/
-
-export default Component.extend({
- layout,
-});
diff --git a/addons/rose/addon/components/rose/form/checkbox.js b/addons/rose/addon/components/rose/form/checkbox.js
new file mode 100644
index 000000000..5b94c0a1d
--- /dev/null
+++ b/addons/rose/addon/components/rose/form/checkbox.js
@@ -0,0 +1,21 @@
+import Component from '@ember/component';
+import layout from '../../../templates/components/rose/form/checkbox';
+
+/**
+ * A simple HTML `` element.
+ *
+ * @example
+ *
+ */
+export default Component.extend({
+ layout,
+
+ tagName: '',
+});
diff --git a/addons/rose/addon/styles/rose/components/_checkbox.scss b/addons/rose/addon/styles/rose/components/_checkbox.scss
deleted file mode 100644
index cdc844774..000000000
--- a/addons/rose/addon/styles/rose/components/_checkbox.scss
+++ /dev/null
@@ -1,82 +0,0 @@
-@use '../variables/sizing';
-@use '../utilities/type' as typography;
-
-$checkbox-check-height: sizing.rems(xs) - sizing.rems(xxxs);
-$checkbox-check-size: sizing.rems(xxxxs) + (sizing.rems(xxxs) / 3);
-$checkbox-check-left-position: sizing.rems(xxs) - (sizing.rems(xxs) / 3);
-
-.rose-checkbox {
- @include typography.type(m);
- color: var(--stark);
- background-color: var(--subtle);
-
- .rose-checkbox-input {
- -webkit-appearance: none;
- appearance: none;
- display: inline-block;
- position: relative;
- padding: 0;
- margin: 0;
- border: sizing.rems(xxxxs) solid var(--ui-border);
- border-radius: sizing.rems(xxxs);
- width: 14px;
- height: 14px;
- background-color: var(--subtle);
-
- &::before {
- content: '';
- position: absolute;
- left: $checkbox-check-left-position;
- width: sizing.rems(xxxs);
- height: $checkbox-check-height;
- border: solid;
- border-width: 0 $checkbox-check-size $checkbox-check-size 0;
- border-color: var(--info);
- border-radius: sizing.rems(xxxxs);
- -webkit-transform: rotate(45deg);
- transform: rotate(45deg);
- visibility: hidden;
- }
-
- &:checked {
- border-color: var(--info);
- }
-
- &:disabled {
- cursor: not-allowed;
- border-color: var(--ui-border);
- background-color: var(--ui-gray-subtler-4);
-
- &::before {
- border-color: var(--ui-border);
- }
- }
-
- &:checked::before {
- visibility: visible;
- }
-
- &:focus {
- outline: none;
- border-color: var(--info);
- }
-
- &:hover {
- border-color: var(--ui-gray);
- }
- }
-
- &.error {
- .rose-checkbox-input {
- border-color: var(--failure);
-
- &:focus {
- border-color: var(--info);
- }
-
- &:disabled {
- border-color: var(--ui-border);
- }
- }
- }
-}
diff --git a/addons/rose/addon/styles/rose/components/_index.scss b/addons/rose/addon/styles/rose/components/_index.scss
index 2667fb1c7..dd972080b 100644
--- a/addons/rose/addon/styles/rose/components/_index.scss
+++ b/addons/rose/addon/styles/rose/components/_index.scss
@@ -1,3 +1,2 @@
@import 'button';
@import 'form';
-@import 'checkbox';
diff --git a/addons/rose/addon/styles/rose/components/form/_checkbox.scss b/addons/rose/addon/styles/rose/components/form/_checkbox.scss
new file mode 100644
index 000000000..4b826841e
--- /dev/null
+++ b/addons/rose/addon/styles/rose/components/form/_checkbox.scss
@@ -0,0 +1,122 @@
+@use '../../variables/sizing';
+@use '../../utilities/type';
+
+.rose-form-checkbox {
+ position: relative;
+ margin-bottom: sizing.rems(xs);
+ color: var(--ui-gray);
+
+ .rose-form-checkbox-label {
+ // label styles
+ @include type.type(s, semibold);
+ color: currentColor;
+ padding-left: sizing.rems(l);
+
+ // custom checkbox styles
+ &::before {
+ position: absolute;
+ top: sizing.rems(xxxs);
+ left: 0;
+ display: block;
+ width: sizing.rems(m);
+ height: sizing.rems(m);
+ pointer-events: none;
+ content: ' ';
+ border: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2);
+ border-radius: sizing.rems(xxxs);
+ }
+
+ &::after {
+ position: absolute;
+ top: -#{sizing.rems(xxxxs)};
+ left: -#{sizing.rems(xxxs) + sizing.rems(xxxxs)};
+ display: block;
+ width: sizing.rems(l);
+ height: sizing.rems(l);
+ transform: scale(0.9);
+ pointer-events: none;
+ content: ' ';
+ background: no-repeat 50%/50% 50%;
+ }
+ }
+
+ .rose-form-checkbox-field {
+ // mostly this just visually hides the native checkbox
+ position: absolute;
+ left: 0;
+ z-index: -1;
+ width: sizing.rems(m);
+ height: sizing.rems(m);
+ opacity: 0;
+
+ // Visual states for custom checkbox.
+ // Yeah. It's complicated.
+ &:not(:disabled) {
+ &:hover {
+ ~ .rose-form-checkbox-label {
+ &::before {
+ border-color: var(--ui-gray);
+ }
+ }
+ }
+
+ &:focus {
+ ~ .rose-form-checkbox-label {
+ &::before {
+ border-color: var(--info);
+ }
+ }
+ }
+
+ &:checked {
+ ~ .rose-form-checkbox-label {
+ &::before {
+ border-color: var(--info);
+ }
+ &::after {
+ background-image: url("data:image/svg+xml,");
+ }
+ }
+ }
+ }
+
+ &:disabled {
+ ~ .rose-form-checkbox-label {
+ &::before {
+ border-color: var(--ui-gray-subtler-2);
+ background-color: var(--ui-gray-subtler-4);
+ }
+ }
+
+ &:checked {
+ ~ .rose-form-checkbox-label {
+ &::after {
+ background-image: url("data:image/svg+xml,");
+ }
+ }
+ }
+ }
+ }
+
+ &.error {
+ color: var(--failure);
+
+ .rose-form-checkbox-field {
+ &:not(:disabled):not(:focus) {
+ ~ .rose-form-checkbox-label {
+ &::before {
+ border-color: var(--failure);
+ }
+ }
+
+ &:checked {
+ ~ .rose-form-checkbox-label {
+ &::after {
+ background-image: url("data:image/svg+xml,");
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/addons/rose/addon/styles/rose/components/form/_index.scss b/addons/rose/addon/styles/rose/components/form/_index.scss
index fdc60b1cd..55ce75517 100644
--- a/addons/rose/addon/styles/rose/components/form/_index.scss
+++ b/addons/rose/addon/styles/rose/components/form/_index.scss
@@ -1 +1,2 @@
@import 'input';
+@import 'checkbox';
diff --git a/addons/rose/addon/styles/rose/variables/_sizing.scss b/addons/rose/addon/styles/rose/variables/_sizing.scss
index 93ebe4da7..db29d6a96 100644
--- a/addons/rose/addon/styles/rose/variables/_sizing.scss
+++ b/addons/rose/addon/styles/rose/variables/_sizing.scss
@@ -23,7 +23,7 @@ $size-coefficients: (
// 4
xxxs: 0.125,
// 2
- xxxxs: 0.0625 // 1,,,
+ xxxxs: 0.0625 // 1,,,,
);
/**
diff --git a/addons/rose/addon/styles/rose/variables/_typography.scss b/addons/rose/addon/styles/rose/variables/_typography.scss
index beaf59a5a..356954b3e 100644
--- a/addons/rose/addon/styles/rose/variables/_typography.scss
+++ b/addons/rose/addon/styles/rose/variables/_typography.scss
@@ -40,7 +40,7 @@ $font-sizes: (
// 20
h3: sizing.size(m),
// 16
- button: sizing.size(m) * 0.8125 // 13,,,
+ button: sizing.size(m) * 0.8125 // 13,,,,
);
// Line height expressed as ratio.
diff --git a/addons/rose/addon/templates/components/rose/checkbox.hbs b/addons/rose/addon/templates/components/rose/checkbox.hbs
deleted file mode 100644
index 3037e7218..000000000
--- a/addons/rose/addon/templates/components/rose/checkbox.hbs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
diff --git a/addons/rose/addon/templates/components/rose/form/checkbox.hbs b/addons/rose/addon/templates/components/rose/form/checkbox.hbs
new file mode 100644
index 000000000..ab2dec73b
--- /dev/null
+++ b/addons/rose/addon/templates/components/rose/form/checkbox.hbs
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ {{yield}}
+
+
diff --git a/addons/rose/app/components/rose/checkbox.js b/addons/rose/app/components/rose/checkbox.js
deleted file mode 100644
index d3323b60d..000000000
--- a/addons/rose/app/components/rose/checkbox.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from 'rose/components/rose/checkbox';
diff --git a/addons/rose/app/components/rose/form/checkbox.js b/addons/rose/app/components/rose/form/checkbox.js
new file mode 100644
index 000000000..fecc9b373
--- /dev/null
+++ b/addons/rose/app/components/rose/form/checkbox.js
@@ -0,0 +1 @@
+export { default } from 'rose/components/rose/form/checkbox';
diff --git a/addons/rose/app/templates/index.hbs b/addons/rose/app/templates/index.hbs
index 4e4192bca..c68de7675 100644
--- a/addons/rose/app/templates/index.hbs
+++ b/addons/rose/app/templates/index.hbs
@@ -64,8 +64,28 @@
/>
Checkbox
-Label
-Error
-Checked
-Disabled
-Disabled + Checked
+
+
+
+
+
diff --git a/addons/rose/stories/checkbox.stories.js b/addons/rose/stories/form/checkbox.stories.js
similarity index 56%
rename from addons/rose/stories/checkbox.stories.js
rename to addons/rose/stories/form/checkbox.stories.js
index 32a978225..2292dd816 100644
--- a/addons/rose/stories/checkbox.stories.js
+++ b/addons/rose/stories/form/checkbox.stories.js
@@ -2,29 +2,26 @@ import { hbs } from 'ember-cli-htmlbars';
import { text, boolean } from '@storybook/addon-knobs';
export default {
- title: 'Rose::Checkbox',
+ title: 'Rose::Form::Checkbox',
};
export const Normal = () => ({
template: hbs`
-
- {{label}}
-
+ disabled={{disabled}}
+ />
`,
context: {
label: text('text', 'Checkbox 🛎'),
- id: text('text', 'Identifier'),
- name: text('text', 'name'),
- form: text('text', 'form'),
+ id: text('id', 'checkbox-1'),
+ name: text('name', 'checkbox-1'),
checked: boolean('checked', false),
+ error: boolean('error', false),
disabled: boolean('disabled', false),
- error: boolean('error', false)
},
});
diff --git a/addons/rose/tests/integration/components/rose/checkbox-test.js b/addons/rose/tests/integration/components/rose/checkbox-test.js
deleted file mode 100644
index 44b5b895c..000000000
--- a/addons/rose/tests/integration/components/rose/checkbox-test.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { module, test } from 'qunit';
-import { setupRenderingTest } from 'ember-qunit';
-import { render } from '@ember/test-helpers';
-import hbs from 'htmlbars-inline-precompile';
-
-module('Integration | Component | rose/checkbox', function (hooks) {
- setupRenderingTest(hooks);
-
- test('it renders without label', async function (assert) {
- await render(hbs``);
- assert.equal(this.element.textContent.trim(), '');
- });
-
- test('it renders a checkbox', async function (assert) {
- await render(hbs``);
- assert.equal(this.element.querySelector('input').type, 'checkbox');
- });
-
- test('it renders with label', async function (assert) {
- await render(hbs`Label`);
- assert.equal(this.element.textContent.trim(), 'Label');
- });
-
- test('it renders in a div wrapper', async function (assert) {
- await render(hbs``);
- assert.ok(this.element.querySelector('div.rose-checkbox'));
- });
-
- test('it renders a checkbox class', async function (assert) {
- await render(hbs``);
- assert.ok(this.element.querySelector('input.rose-checkbox-input'));
- });
-
- test('it renders a label class', async function (assert) {
- await render(hbs``);
- assert.ok(this.element.querySelector('label.rose-checkbox-label'));
- });
-
- test('it is not checked', async function (assert) {
- await render(hbs``);
- assert.equal(this.element.querySelector('input').checked, false);
- });
-
- test('it is not disabled', async function (assert) {
- await render(hbs``);
- assert.equal(this.element.querySelector('input').disabled, false);
- });
-
- test('it is checked when @checked={{true}}', async function (assert) {
- await render(hbs``);
- assert.equal(this.element.querySelector('input').checked, true);
- });
-
- test('it marks error when @error={{true}}', async function (assert) {
- await render(hbs``);
- assert.ok(this.element.querySelector('div.error'));
- });
-
- test('it is disabled when @disabled={{true}}', async function (assert) {
- await render(hbs``);
- assert.equal(this.element.querySelector('input').disabled, true);
- });
-
- test('it is disabled and checked when @disabled={{true}} and @checked={{true}}', async function (assert) {
- await render(
- hbs``
- );
- assert.equal(this.element.querySelector('input').checked, true);
- assert.equal(this.element.querySelector('input').disabled, true);
- });
-});
diff --git a/addons/rose/tests/integration/components/rose/form/checkbox-test.js b/addons/rose/tests/integration/components/rose/form/checkbox-test.js
new file mode 100644
index 000000000..e341dd0db
--- /dev/null
+++ b/addons/rose/tests/integration/components/rose/form/checkbox-test.js
@@ -0,0 +1,39 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, find } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+
+module('Integration | Component | rose/form/checkbox', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ await render(hbs``);
+ assert.equal(await find('label').textContent.trim(), 'Label');
+ assert.ok(await find('input'));
+ });
+
+ test('it is not checked by default', async function (assert) {
+ await render(hbs``);
+ assert.equal(await find('input').checked, false);
+ });
+
+ test('it is not disabled by default', async function (assert) {
+ await render(hbs``);
+ assert.equal(await find('input').disabled, false);
+ });
+
+ test('it is checked when @checked={{true}}', async function (assert) {
+ await render(hbs``);
+ assert.equal(await find('input').checked, true);
+ });
+
+ test('it marks error when @error={{true}}', async function (assert) {
+ await render(hbs``);
+ assert.ok(await find('.error'));
+ });
+
+ test('it is disabled when disabled={{true}}', async function (assert) {
+ await render(hbs``);
+ assert.equal(await find('input').disabled, true);
+ });
+});