@sproutsocial/seeds-react-loader 1.0.23 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -8,33 +8,12 @@ import React2 from "react";
8
8
  import styled, { css, keyframes } from "styled-components";
9
9
  import { COMMON } from "@sproutsocial/seeds-react-system-props";
10
10
  import { visuallyHidden } from "@sproutsocial/seeds-react-mixins";
11
- var getSize = (size) => {
12
- const spinnerSize = Math.round(size * 1);
13
- const borderWidth = Math.round(size * 0.1);
14
- return css`
15
- width: ${size}px;
16
- height: ${size}px;
17
- box-shadow: ${({ theme }) => `0 0 0 2px ${theme.colors.neutral[600]},
18
- inset 0 0 0 ${borderWidth + 2}px ${theme.colors.neutral[600]}`};
19
-
20
- &:after {
21
- width: ${spinnerSize}px;
22
- height: ${spinnerSize}px;
23
- box-sizing: border-box;
24
- border-width: ${borderWidth}px;
25
- }
26
-
27
- .no-cssanimations &::after {
28
- background-size: ${spinnerSize}px;
29
- }
30
- `;
31
- };
32
11
  var spin = keyframes`
33
12
  from {
34
- transform: translate(-50%,-50%) rotate(0deg);
13
+ transform: rotate(0deg);
35
14
  }
36
15
  to {
37
- transform: translate(-50%,-50%) rotate(360deg);
16
+ transform: rotate(360deg);
38
17
  }
39
18
  `;
40
19
  var delayAnimation = keyframes`
@@ -48,39 +27,70 @@ var delayAnimation = keyframes`
48
27
  opacity: 1;
49
28
  }
50
29
  `;
30
+ var getContainerSize = (size) => css`
31
+ width: ${size}px;
32
+ height: ${size}px;
33
+ `;
34
+ var getSpinnerSize = (size) => {
35
+ const borderWidth = Math.round(size * 0.1);
36
+ const half = Math.round(size / 2);
37
+ return css`
38
+ width: ${size}px;
39
+ height: ${size}px;
40
+ clip: rect(${half}px, ${size}px, ${size}px, 0);
41
+
42
+ &::before,
43
+ &::after {
44
+ width: ${size}px;
45
+ height: ${size}px;
46
+ border-width: ${borderWidth}px;
47
+ }
48
+ `;
49
+ };
51
50
  var Container = styled.div.attrs({
52
51
  className: "Loader"
53
52
  })`
54
53
  position: relative;
55
54
  margin: 0 auto;
56
55
  padding: 0;
57
- overflow: hidden;
58
- border-radius: 100%;
59
56
 
60
57
  animation: ${(props) => props.delay ? css`
61
- ${delayAnimation} 2s 1;
62
- ` : "none"};
58
+ ${spin} 3s infinite linear, ${delayAnimation} 2s 1
59
+ ` : css`
60
+ ${spin} 3s infinite linear
61
+ `};
63
62
 
64
- &:after {
63
+ ${(props) => props.small ? getContainerSize(20) : getContainerSize(40)}
64
+ ${COMMON}
65
+ `;
66
+ var Spinner = styled.span`
67
+ position: absolute;
68
+ inset: 0;
69
+ margin: auto;
70
+ animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
71
+
72
+ &::before,
73
+ &::after {
74
+ content: "";
65
75
  position: absolute;
66
- top: 50%;
67
- left: 50%;
68
- background: transparent;
76
+ inset: 0;
77
+ margin: auto;
78
+ box-sizing: border-box;
69
79
  border-style: solid;
70
80
  border-radius: 100%;
71
- content: "";
72
- transition: opacity 250ms;
73
- border-color: ${({ theme }) => `${theme.colors.neutral[0]} ${theme.colors.neutral[0]} ${theme.colors.neutral[600]} ${theme.colors.neutral[600]}`};
81
+ }
74
82
 
75
- animation: ${(props) => props.delay ? css`
76
- ${spin} 2.25s infinite linear, ${delayAnimation} 2s 1
77
- ` : css`
78
- ${spin} 2.25s infinite linear
79
- `};
83
+ &::before {
84
+ border-color: transparent;
85
+ border-top-color: ${({ theme }) => theme.colors.neutral[0]};
86
+ animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
80
87
  }
81
88
 
82
- ${(props) => props.small ? getSize(20) : getSize(40)}
83
- ${COMMON}
89
+ &::after {
90
+ border-color: ${({ theme }) => theme.colors.neutral[600]};
91
+ }
92
+
93
+ ${(props) => props.small ? getSpinnerSize(20) : getSpinnerSize(40)}
84
94
  `;
85
95
  var Text = styled.div`
86
96
  ${visuallyHidden}
@@ -89,7 +99,7 @@ var styles_default = Container;
89
99
 
90
100
  // src/LoaderTailwind.tsx
91
101
  import React from "react";
92
- import { jsx } from "react/jsx-runtime";
102
+ import { jsx, jsxs } from "react/jsx-runtime";
93
103
  function cn(...inputs) {
94
104
  const classes = [];
95
105
  for (const input of inputs) {
@@ -127,7 +137,10 @@ var LoaderTailwind = React.forwardRef(
127
137
  return (
128
138
  // QA default precedes {...rest} so a consumer-provided data-qa-loader
129
139
  // (via {...rest} or the qa prop) overrides the default.
130
- /* @__PURE__ */ jsx("div", { "data-qa-loader": "", ...rest, className: classes, ref, ...qa, children: /* @__PURE__ */ jsx("span", { className: "seeds-loader-visually-hidden", children: text || "Loading" }) })
140
+ /* @__PURE__ */ jsxs("div", { "data-qa-loader": "", ...rest, className: classes, ref, ...qa, children: [
141
+ /* @__PURE__ */ jsx("span", { className: "seeds-loader-spinner", "aria-hidden": "true" }),
142
+ /* @__PURE__ */ jsx("span", { className: "seeds-loader-visually-hidden", children: text || "Loading" })
143
+ ] })
131
144
  );
132
145
  }
133
146
  );
@@ -135,7 +148,7 @@ LoaderTailwind.displayName = "LoaderTailwind";
135
148
 
136
149
  // src/LoaderHybrid.tsx
137
150
  import { hasStyledProps } from "@sproutsocial/seeds-react-system-props";
138
- import { jsx as jsx2 } from "react/jsx-runtime";
151
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
139
152
  var LoaderHybrid = React2.forwardRef(
140
153
  (props, ref) => {
141
154
  const {
@@ -147,7 +160,7 @@ var LoaderHybrid = React2.forwardRef(
147
160
  ...rest
148
161
  } = props;
149
162
  if (hasStyledProps(rest)) {
150
- return /* @__PURE__ */ jsx2(
163
+ return /* @__PURE__ */ jsxs2(
151
164
  styles_default,
152
165
  {
153
166
  small: size === "small",
@@ -157,7 +170,10 @@ var LoaderHybrid = React2.forwardRef(
157
170
  ref,
158
171
  ...qa,
159
172
  ...rest,
160
- children: /* @__PURE__ */ jsx2(Text, { children: text || "Loading" })
173
+ children: [
174
+ /* @__PURE__ */ jsx2(Spinner, { small: size === "small", "aria-hidden": "true" }),
175
+ /* @__PURE__ */ jsx2(Text, { children: text || "Loading" })
176
+ ]
161
177
  }
162
178
  );
163
179
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Loader.tsx","../../src/LoaderHybrid.tsx","../../src/styles.ts","../../src/LoaderTailwind.tsx","../../src/index.ts"],"sourcesContent":["import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst getSize = (size: number) => {\n const spinnerSize = Math.round(size * 1);\n const borderWidth = Math.round(size * 0.1);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n box-shadow: ${({ theme }) =>\n `0 0 0 2px ${theme.colors.neutral[600]}, \n inset 0 0 0 ${borderWidth + 2}px ${theme.colors.neutral[600]}`};\n\n &:after {\n width: ${spinnerSize}px;\n height: ${spinnerSize}px;\n box-sizing: border-box;\n border-width: ${borderWidth}px;\n }\n\n .no-cssanimations &::after {\n background-size: ${spinnerSize}px;\n }\n `;\n};\n\nconst spin = keyframes`\n from {\n transform: translate(-50%,-50%) rotate(0deg);\n }\n to {\n transform: translate(-50%,-50%) rotate(360deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n overflow: hidden;\n border-radius: 100%;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${delayAnimation} 2s 1;\n `\n : \"none\"};\n\n &:after {\n position: absolute;\n top: 50%;\n left: 50%;\n background: transparent;\n border-style: solid;\n border-radius: 100%;\n content: \"\";\n transition: opacity 250ms;\n border-color: ${({ theme }) =>\n `${theme.colors.neutral[0]} ${theme.colors.neutral[0]} ${theme.colors.neutral[600]} ${theme.colors.neutral[600]}`};\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${spin} 2.25s infinite linear, ${delayAnimation} 2s 1\n `\n : css`\n ${spin} 2.25s infinite linear\n `};\n }\n\n ${(props) => (props.small ? getSize(20) : getSize(40))}\n ${COMMON}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n","import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n"],"mappings":"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","names":["React","React","jsx","React","jsx","React"]}
1
+ {"version":3,"sources":["../../src/Loader.tsx","../../src/LoaderHybrid.tsx","../../src/styles.ts","../../src/LoaderTailwind.tsx","../../src/index.ts"],"sourcesContent":["import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Spinner, Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Spinner small={size === \"small\"} aria-hidden=\"true\" />\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\n// Size for the container box.\nconst getContainerSize = (size: number) => css`\n width: ${size}px;\n height: ${size}px;\n`;\n\n// Size for the visual spinner: dimensions, the clipped half, and the arc/ring\n// border widths all scale together so the arc clips correctly at every size.\nconst getSpinnerSize = (size: number) => {\n const borderWidth = Math.round(size * 0.1);\n const half = Math.round(size / 2);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n clip: rect(${half}px, ${size}px, ${size}px, 0);\n\n &::before,\n &::after {\n width: ${size}px;\n height: ${size}px;\n border-width: ${borderWidth}px;\n }\n `;\n};\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${spin} 3s infinite linear, ${delayAnimation} 2s 1\n `\n : css`\n ${spin} 3s infinite linear\n `};\n\n ${(props) => (props.small ? getContainerSize(20) : getContainerSize(40))}\n ${COMMON}\n`;\n\n// Visual spinner — the bright rotating arc (&::before) and faint arc ring\n// (&::after), both clipped to the bottom half. Hidden from assistive tech\n// via aria-hidden in markup.\nexport const Spinner = styled.span<{ small?: boolean }>`\n position: absolute;\n inset: 0;\n margin: auto;\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n\n &::before,\n &::after {\n content: \"\";\n position: absolute;\n inset: 0;\n margin: auto;\n box-sizing: border-box;\n border-style: solid;\n border-radius: 100%;\n }\n\n &::before {\n border-color: transparent;\n border-top-color: ${({ theme }) => theme.colors.neutral[0]};\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n }\n\n &::after {\n border-color: ${({ theme }) => theme.colors.neutral[600]};\n }\n\n ${(props) => (props.small ? getSpinnerSize(20) : getSpinnerSize(40))}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n {/* visual spinner — hosts the ::before (arc) pseudo-element;\n hidden from assistive tech. */}\n <span className=\"seeds-loader-spinner\" aria-hidden=\"true\" />\n {/* a11y label — must stay discoverable by getByText(\"Loading\"). */}\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n","import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n"],"mappings":"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","names":["React","React","jsx","jsxs","React","jsx","React"]}
package/dist/index.js CHANGED
@@ -45,33 +45,12 @@ var import_react2 = __toESM(require("react"));
45
45
  var import_styled_components = __toESM(require("styled-components"));
46
46
  var import_seeds_react_system_props = require("@sproutsocial/seeds-react-system-props");
47
47
  var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
48
- var getSize = (size) => {
49
- const spinnerSize = Math.round(size * 1);
50
- const borderWidth = Math.round(size * 0.1);
51
- return import_styled_components.css`
52
- width: ${size}px;
53
- height: ${size}px;
54
- box-shadow: ${({ theme }) => `0 0 0 2px ${theme.colors.neutral[600]},
55
- inset 0 0 0 ${borderWidth + 2}px ${theme.colors.neutral[600]}`};
56
-
57
- &:after {
58
- width: ${spinnerSize}px;
59
- height: ${spinnerSize}px;
60
- box-sizing: border-box;
61
- border-width: ${borderWidth}px;
62
- }
63
-
64
- .no-cssanimations &::after {
65
- background-size: ${spinnerSize}px;
66
- }
67
- `;
68
- };
69
48
  var spin = import_styled_components.keyframes`
70
49
  from {
71
- transform: translate(-50%,-50%) rotate(0deg);
50
+ transform: rotate(0deg);
72
51
  }
73
52
  to {
74
- transform: translate(-50%,-50%) rotate(360deg);
53
+ transform: rotate(360deg);
75
54
  }
76
55
  `;
77
56
  var delayAnimation = import_styled_components.keyframes`
@@ -85,39 +64,70 @@ var delayAnimation = import_styled_components.keyframes`
85
64
  opacity: 1;
86
65
  }
87
66
  `;
67
+ var getContainerSize = (size) => import_styled_components.css`
68
+ width: ${size}px;
69
+ height: ${size}px;
70
+ `;
71
+ var getSpinnerSize = (size) => {
72
+ const borderWidth = Math.round(size * 0.1);
73
+ const half = Math.round(size / 2);
74
+ return import_styled_components.css`
75
+ width: ${size}px;
76
+ height: ${size}px;
77
+ clip: rect(${half}px, ${size}px, ${size}px, 0);
78
+
79
+ &::before,
80
+ &::after {
81
+ width: ${size}px;
82
+ height: ${size}px;
83
+ border-width: ${borderWidth}px;
84
+ }
85
+ `;
86
+ };
88
87
  var Container = import_styled_components.default.div.attrs({
89
88
  className: "Loader"
90
89
  })`
91
90
  position: relative;
92
91
  margin: 0 auto;
93
92
  padding: 0;
94
- overflow: hidden;
95
- border-radius: 100%;
96
93
 
97
94
  animation: ${(props) => props.delay ? import_styled_components.css`
98
- ${delayAnimation} 2s 1;
99
- ` : "none"};
95
+ ${spin} 3s infinite linear, ${delayAnimation} 2s 1
96
+ ` : import_styled_components.css`
97
+ ${spin} 3s infinite linear
98
+ `};
99
+
100
+ ${(props) => props.small ? getContainerSize(20) : getContainerSize(40)}
101
+ ${import_seeds_react_system_props.COMMON}
102
+ `;
103
+ var Spinner = import_styled_components.default.span`
104
+ position: absolute;
105
+ inset: 0;
106
+ margin: auto;
107
+ animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
100
108
 
101
- &:after {
109
+ &::before,
110
+ &::after {
111
+ content: "";
102
112
  position: absolute;
103
- top: 50%;
104
- left: 50%;
105
- background: transparent;
113
+ inset: 0;
114
+ margin: auto;
115
+ box-sizing: border-box;
106
116
  border-style: solid;
107
117
  border-radius: 100%;
108
- content: "";
109
- transition: opacity 250ms;
110
- border-color: ${({ theme }) => `${theme.colors.neutral[0]} ${theme.colors.neutral[0]} ${theme.colors.neutral[600]} ${theme.colors.neutral[600]}`};
118
+ }
111
119
 
112
- animation: ${(props) => props.delay ? import_styled_components.css`
113
- ${spin} 2.25s infinite linear, ${delayAnimation} 2s 1
114
- ` : import_styled_components.css`
115
- ${spin} 2.25s infinite linear
116
- `};
120
+ &::before {
121
+ border-color: transparent;
122
+ border-top-color: ${({ theme }) => theme.colors.neutral[0]};
123
+ animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
117
124
  }
118
125
 
119
- ${(props) => props.small ? getSize(20) : getSize(40)}
120
- ${import_seeds_react_system_props.COMMON}
126
+ &::after {
127
+ border-color: ${({ theme }) => theme.colors.neutral[600]};
128
+ }
129
+
130
+ ${(props) => props.small ? getSpinnerSize(20) : getSpinnerSize(40)}
121
131
  `;
122
132
  var Text = import_styled_components.default.div`
123
133
  ${import_seeds_react_mixins.visuallyHidden}
@@ -126,7 +136,11 @@ var styles_default = Container;
126
136
 
127
137
  // src/LoaderTailwind.tsx
128
138
  var import_react = __toESM(require("react"));
129
- var import_jsx_runtime = require("react/jsx-runtime");
139
+ var import_jsx_runtime = (
140
+ // QA default precedes {...rest} so a consumer-provided data-qa-loader
141
+ // (via {...rest} or the qa prop) overrides the default.
142
+ require("react/jsx-runtime")
143
+ );
130
144
  function cn(...inputs) {
131
145
  const classes = [];
132
146
  for (const input of inputs) {
@@ -161,11 +175,10 @@ var LoaderTailwind = import_react.default.forwardRef(
161
175
  { "seeds-loader-delay": delay },
162
176
  className
163
177
  );
164
- return (
165
- // QA default precedes {...rest} so a consumer-provided data-qa-loader
166
- // (via {...rest} or the qa prop) overrides the default.
167
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-qa-loader": "", ...rest, className: classes, ref, ...qa, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "seeds-loader-visually-hidden", children: text || "Loading" }) })
168
- );
178
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-qa-loader": "", ...rest, className: classes, ref, ...qa, children: [
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "seeds-loader-spinner", "aria-hidden": "true" }),
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "seeds-loader-visually-hidden", children: text || "Loading" })
181
+ ] });
169
182
  }
170
183
  );
171
184
  LoaderTailwind.displayName = "LoaderTailwind";
@@ -184,7 +197,7 @@ var LoaderHybrid = import_react2.default.forwardRef(
184
197
  ...rest
185
198
  } = props;
186
199
  if ((0, import_seeds_react_system_props2.hasStyledProps)(rest)) {
187
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
200
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
188
201
  styles_default,
189
202
  {
190
203
  small: size === "small",
@@ -194,7 +207,10 @@ var LoaderHybrid = import_react2.default.forwardRef(
194
207
  ref,
195
208
  ...qa,
196
209
  ...rest,
197
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: text || "Loading" })
210
+ children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Spinner, { small: size === "small", "aria-hidden": "true" }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: text || "Loading" })
213
+ ]
198
214
  }
199
215
  );
200
216
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Loader.tsx","../src/LoaderHybrid.tsx","../src/styles.ts","../src/LoaderTailwind.tsx"],"sourcesContent":["import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n","import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst getSize = (size: number) => {\n const spinnerSize = Math.round(size * 1);\n const borderWidth = Math.round(size * 0.1);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n box-shadow: ${({ theme }) =>\n `0 0 0 2px ${theme.colors.neutral[600]}, \n inset 0 0 0 ${borderWidth + 2}px ${theme.colors.neutral[600]}`};\n\n &:after {\n width: ${spinnerSize}px;\n height: ${spinnerSize}px;\n box-sizing: border-box;\n border-width: ${borderWidth}px;\n }\n\n .no-cssanimations &::after {\n background-size: ${spinnerSize}px;\n }\n `;\n};\n\nconst spin = keyframes`\n from {\n transform: translate(-50%,-50%) rotate(0deg);\n }\n to {\n transform: translate(-50%,-50%) rotate(360deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n overflow: hidden;\n border-radius: 100%;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${delayAnimation} 2s 1;\n `\n : \"none\"};\n\n &:after {\n position: absolute;\n top: 50%;\n left: 50%;\n background: transparent;\n border-style: solid;\n border-radius: 100%;\n content: \"\";\n transition: opacity 250ms;\n border-color: ${({ theme }) =>\n `${theme.colors.neutral[0]} ${theme.colors.neutral[0]} ${theme.colors.neutral[600]} ${theme.colors.neutral[600]}`};\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${spin} 2.25s infinite linear, ${delayAnimation} 2s 1\n `\n : css`\n ${spin} 2.25s infinite linear\n `};\n }\n\n ${(props) => (props.small ? getSize(20) : getSize(40))}\n ${COMMON}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n"],"mappings":"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","names":["import_react","import_react","styled","React","import_seeds_react_system_props","import_jsx_runtime","React","import_jsx_runtime","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Loader.tsx","../src/LoaderHybrid.tsx","../src/styles.ts","../src/LoaderTailwind.tsx"],"sourcesContent":["import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n","import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Spinner, Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Spinner small={size === \"small\"} aria-hidden=\"true\" />\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\n// Size for the container box.\nconst getContainerSize = (size: number) => css`\n width: ${size}px;\n height: ${size}px;\n`;\n\n// Size for the visual spinner: dimensions, the clipped half, and the arc/ring\n// border widths all scale together so the arc clips correctly at every size.\nconst getSpinnerSize = (size: number) => {\n const borderWidth = Math.round(size * 0.1);\n const half = Math.round(size / 2);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n clip: rect(${half}px, ${size}px, ${size}px, 0);\n\n &::before,\n &::after {\n width: ${size}px;\n height: ${size}px;\n border-width: ${borderWidth}px;\n }\n `;\n};\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${spin} 3s infinite linear, ${delayAnimation} 2s 1\n `\n : css`\n ${spin} 3s infinite linear\n `};\n\n ${(props) => (props.small ? getContainerSize(20) : getContainerSize(40))}\n ${COMMON}\n`;\n\n// Visual spinner — the bright rotating arc (&::before) and faint arc ring\n// (&::after), both clipped to the bottom half. Hidden from assistive tech\n// via aria-hidden in markup.\nexport const Spinner = styled.span<{ small?: boolean }>`\n position: absolute;\n inset: 0;\n margin: auto;\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n\n &::before,\n &::after {\n content: \"\";\n position: absolute;\n inset: 0;\n margin: auto;\n box-sizing: border-box;\n border-style: solid;\n border-radius: 100%;\n }\n\n &::before {\n border-color: transparent;\n border-top-color: ${({ theme }) => theme.colors.neutral[0]};\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n }\n\n &::after {\n border-color: ${({ theme }) => theme.colors.neutral[600]};\n }\n\n ${(props) => (props.small ? getSpinnerSize(20) : getSpinnerSize(40))}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n {/* visual spinner — hosts the ::before (arc) pseudo-element;\n hidden from assistive tech. */}\n <span className=\"seeds-loader-spinner\" aria-hidden=\"true\" />\n {/* a11y label — must stay discoverable by getByText(\"Loading\"). */}\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n"],"mappings":"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","names":["import_react","import_react","styled","React","import_seeds_react_system_props","import_jsx_runtime","React","import_jsx_runtime","React"]}
package/dist/loader.css CHANGED
@@ -7,19 +7,23 @@
7
7
  *
8
8
  * Usage:
9
9
  * <div class="seeds-loader seeds-loader-delay">
10
+ * <span class="seeds-loader-spinner" aria-hidden="true"></span>
10
11
  * <span class="seeds-loader-visually-hidden">Loading</span>
11
12
  * </div>
12
13
  * <div class="seeds-loader seeds-loader-sm">...</div> (small)
13
14
  * <div class="seeds-loader">...</div> (no delay)
15
+ *
16
+ * The visual is a thin rotating arc (--color-neutral-0) over a faint arc ring
17
+ * (--color-neutral-600), both clipped to the bottom half by the spinner span.
14
18
  */
15
19
 
16
20
  @layer components {
17
21
  @keyframes seeds-loader-spin {
18
22
  from {
19
- transform: translate(-50%, -50%) rotate(0deg);
23
+ transform: rotate(0deg);
20
24
  }
21
25
  to {
22
- transform: translate(-50%, -50%) rotate(360deg);
26
+ transform: rotate(360deg);
23
27
  }
24
28
  }
25
29
 
@@ -35,70 +39,84 @@
35
39
  }
36
40
  }
37
41
 
38
- /* Base (large / 40px) */
42
+ /* Base (large / 40px) — slow outer rotation hosting the spinner */
39
43
  .seeds-loader {
40
44
  position: relative;
41
45
  margin: 0 auto;
42
46
  padding: 0;
43
- overflow: hidden;
44
- border-radius: 100%;
45
47
  width: 40px;
46
48
  height: 40px;
47
- box-shadow:
48
- 0 0 0 2px var(--color-neutral-600),
49
- inset 0 0 0 6px var(--color-neutral-600);
49
+ animation: seeds-loader-spin 3s infinite linear;
50
50
  }
51
51
 
52
- .seeds-loader::after {
52
+ /* Visual spinner — clipped to bottom half, faster eased rotation */
53
+ .seeds-loader-spinner {
53
54
  position: absolute;
54
- top: 50%;
55
- left: 50%;
55
+ inset: 0;
56
+ margin: auto;
57
+ width: 40px;
58
+ height: 40px;
59
+ clip: rect(20px, 40px, 40px, 0);
60
+ animation: seeds-loader-spin 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
61
+ }
62
+
63
+ /* Bright arc */
64
+ .seeds-loader-spinner::before {
65
+ content: "";
66
+ position: absolute;
67
+ inset: 0;
68
+ margin: auto;
56
69
  width: 40px;
57
70
  height: 40px;
58
71
  box-sizing: border-box;
59
- border-width: 4px;
60
- background: transparent;
61
- border-style: solid;
72
+ border: 4px solid transparent;
73
+ border-top-color: var(--color-neutral-0);
62
74
  border-radius: 100%;
63
- content: "";
64
- transition: opacity 250ms;
65
- border-color: var(--color-neutral-0) var(--color-neutral-0)
66
- var(--color-neutral-600) var(--color-neutral-600);
67
- animation: seeds-loader-spin 2.25s infinite linear;
75
+ animation: seeds-loader-spin 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
68
76
  }
69
77
 
70
- .no-cssanimations .seeds-loader::after {
71
- background-size: 40px;
78
+ /* Faint arc ring (static) — clipped to bottom half alongside the bright arc */
79
+ .seeds-loader-spinner::after {
80
+ content: "";
81
+ position: absolute;
82
+ inset: 0;
83
+ margin: auto;
84
+ width: 40px;
85
+ height: 40px;
86
+ box-sizing: border-box;
87
+ border: 4px solid var(--color-neutral-600);
88
+ border-radius: 100%;
72
89
  }
73
90
 
74
- /* Delay variant adds the delay fade-in to the container and the spinner */
75
- .seeds-loader-delay {
76
- animation: seeds-loader-delay-anim 2s 1;
91
+ /* When animations are disabled, the faint arc still renders so the loader
92
+ is never invisible. */
93
+ .no-cssanimations .seeds-loader-spinner::after {
94
+ border-color: var(--color-neutral-600);
77
95
  }
78
96
 
79
- .seeds-loader-delay::after {
97
+ /* Delay variant — fades the whole spinner in alongside the outer rotation */
98
+ .seeds-loader-delay {
80
99
  animation:
81
- seeds-loader-spin 2.25s infinite linear,
100
+ seeds-loader-spin 3s infinite linear,
82
101
  seeds-loader-delay-anim 2s 1;
83
102
  }
84
103
 
85
- /* Small (20px) */
86
- .seeds-loader-sm {
104
+ /* Small (20px) — scale size, border width, and the clip rect together */
105
+ .seeds-loader-sm,
106
+ .seeds-loader-sm .seeds-loader-spinner,
107
+ .seeds-loader-sm .seeds-loader-spinner::before,
108
+ .seeds-loader-sm .seeds-loader-spinner::after {
87
109
  width: 20px;
88
110
  height: 20px;
89
- box-shadow:
90
- 0 0 0 2px var(--color-neutral-600),
91
- inset 0 0 0 4px var(--color-neutral-600);
92
111
  }
93
112
 
94
- .seeds-loader-sm::after {
95
- width: 20px;
96
- height: 20px;
97
- border-width: 2px;
113
+ .seeds-loader-sm .seeds-loader-spinner {
114
+ clip: rect(10px, 20px, 20px, 0);
98
115
  }
99
116
 
100
- .no-cssanimations .seeds-loader-sm::after {
101
- background-size: 20px;
117
+ .seeds-loader-sm .seeds-loader-spinner::before,
118
+ .seeds-loader-sm .seeds-loader-spinner::after {
119
+ border-width: 2px;
102
120
  }
103
121
 
104
122
  /* Visually-hidden accessibility text */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-loader",
3
- "version": "1.0.23",
3
+ "version": "1.0.24",
4
4
  "description": "Seeds React Loader",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",