@sproutsocial/seeds-react-visually-hidden 1.0.32 → 1.0.34

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.
@@ -1,5 +1,5 @@
1
1
  yarn run v1.22.22
2
- $ tsup --dts
2
+ $ tsup --dts && cp src/visually-hidden.css dist/visually-hidden.css
3
3
  CLI Building entry: src/index.ts
4
4
  CLI Using tsconfig: tsconfig.json
5
5
  CLI tsup v8.5.0
@@ -8,14 +8,14 @@ $ tsup --dts
8
8
  CLI Cleaning output folder
9
9
  CJS Build start
10
10
  ESM Build start
11
- CJS dist/index.js 2.11 KB
12
- CJS dist/index.js.map 961.00 B
13
- CJS ⚡️ Build success in 81ms
14
- ESM dist/esm/index.js 402.00 B
15
- ESM dist/esm/index.js.map 842.00 B
11
+ CJS dist/index.js 3.53 KB
12
+ CJS dist/index.js.map 3.37 KB
13
+ CJS ⚡️ Build success in 82ms
14
+ ESM dist/esm/index.js 1.63 KB
15
+ ESM dist/esm/index.js.map 3.24 KB
16
16
  ESM ⚡️ Build success in 81ms
17
17
  DTS Build start
18
- DTS ⚡️ Build success in 4401ms
19
- DTS dist/index.d.ts 481.00 B
20
- DTS dist/index.d.mts 481.00 B
21
- Done in 5.51s.
18
+ DTS ⚡️ Build success in 3776ms
19
+ DTS dist/index.d.ts 482.00 B
20
+ DTS dist/index.d.mts 482.00 B
21
+ Done in 4.84s.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @sproutsocial/seeds-react-visually-hidden
2
2
 
3
+ ## 1.0.34
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [57372fe]
8
+ - @sproutsocial/seeds-react-theme@4.6.0
9
+ - @sproutsocial/seeds-react-mixins@4.3.17
10
+
11
+ ## 1.0.33
12
+
13
+ ### Patch Changes
14
+
15
+ - a33ef79: Add a Tailwind/CSS-class rendering path to VisuallyHidden using the Seeds hybrid pattern. Plain usage now renders the semantic `seeds-visually-hidden` class, while styled-system props and styled-components extensions retain the existing styled-components fallback. The component's public API and loose prop type remain unchanged, and the new CSS ships through package dist and `@sproutsocial/racine/css/components`.
16
+ - @sproutsocial/seeds-react-theme@4.5.1
17
+ - @sproutsocial/seeds-react-mixins@4.3.16
18
+
3
19
  ## 1.0.32
4
20
 
5
21
  ### Patch Changes
package/dist/esm/index.js CHANGED
@@ -1,13 +1,52 @@
1
- // src/VisuallyHidden.tsx
1
+ // src/VisuallyHiddenHybrid.tsx
2
+ import { needsStyledComponents } from "@sproutsocial/seeds-react-system-props";
3
+
4
+ // src/VisuallyHiddenStyled.tsx
2
5
  import styled from "styled-components";
3
6
  import { visuallyHidden } from "@sproutsocial/seeds-react-mixins";
4
7
  import { jsx } from "react/jsx-runtime";
5
8
  var StyledVisuallyHidden = styled.span`
6
9
  ${visuallyHidden}
7
10
  `;
8
- var VisuallyHidden = (props) => {
9
- return /* @__PURE__ */ jsx(StyledVisuallyHidden, { ...props });
11
+ var VisuallyHiddenStyled = (props) => /* @__PURE__ */ jsx(StyledVisuallyHidden, { ...props });
12
+
13
+ // src/VisuallyHiddenTailwind.tsx
14
+ import isPropValid from "@emotion/is-prop-valid";
15
+ import * as React from "react";
16
+ var mergeClassNames = (...classes) => classes.filter(Boolean).join(" ");
17
+ var VisuallyHiddenTailwind = ({
18
+ as,
19
+ children,
20
+ className,
21
+ ...rest
22
+ }) => {
23
+ const Element = as ?? "span";
24
+ const forwardedProps = typeof Element === "string" ? Object.fromEntries(
25
+ Object.entries(rest).filter(([name]) => isPropValid(name))
26
+ ) : rest;
27
+ return React.createElement(
28
+ Element,
29
+ {
30
+ ...forwardedProps,
31
+ className: mergeClassNames("seeds-visually-hidden", className)
32
+ },
33
+ children
34
+ );
35
+ };
36
+ VisuallyHiddenTailwind.displayName = "VisuallyHiddenTailwind";
37
+
38
+ // src/VisuallyHiddenHybrid.tsx
39
+ import { jsx as jsx2 } from "react/jsx-runtime";
40
+ var VisuallyHiddenHybrid = (props) => {
41
+ if (needsStyledComponents(props)) {
42
+ return /* @__PURE__ */ jsx2(VisuallyHiddenStyled, { ...props });
43
+ }
44
+ return /* @__PURE__ */ jsx2(VisuallyHiddenTailwind, { ...props });
10
45
  };
46
+
47
+ // src/VisuallyHidden.tsx
48
+ import { jsx as jsx3 } from "react/jsx-runtime";
49
+ var VisuallyHidden = (props) => /* @__PURE__ */ jsx3(VisuallyHiddenHybrid, { ...props });
11
50
  export {
12
51
  VisuallyHidden
13
52
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/VisuallyHidden.tsx"],"sourcesContent":["import styled from \"styled-components\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nconst StyledVisuallyHidden = styled.span`\n ${visuallyHidden}\n`;\n\nexport type VisuallyHiddenProps = ComponentPropsWithRef<\n typeof StyledVisuallyHidden\n>;\n\n// This wrapper component is needed for react-docgen to work.\n// It has issues with generating docs for the styled component directly.\nexport const VisuallyHidden = (props: VisuallyHiddenProps) => {\n return <StyledVisuallyHidden {...props} />;\n};\n"],"mappings":";AAAA,OAAO,YAAY;AACnB,SAAS,sBAAsB;AActB;AAXT,IAAM,uBAAuB,OAAO;AAAA,IAChC,cAAc;AAAA;AASX,IAAM,iBAAiB,CAAC,UAA+B;AAC5D,SAAO,oBAAC,wBAAsB,GAAG,OAAO;AAC1C;","names":[]}
1
+ {"version":3,"sources":["../../src/VisuallyHiddenHybrid.tsx","../../src/VisuallyHiddenStyled.tsx","../../src/VisuallyHiddenTailwind.tsx","../../src/VisuallyHidden.tsx"],"sourcesContent":["import { needsStyledComponents } from \"@sproutsocial/seeds-react-system-props\";\nimport {\n VisuallyHiddenStyled,\n type VisuallyHiddenProps,\n} from \"./VisuallyHiddenStyled\";\nimport { VisuallyHiddenTailwind } from \"./VisuallyHiddenTailwind\";\n\nexport const VisuallyHiddenHybrid = (props: VisuallyHiddenProps) => {\n if (needsStyledComponents(props)) {\n return <VisuallyHiddenStyled {...props} />;\n }\n\n return <VisuallyHiddenTailwind {...props} />;\n};\n","import styled from \"styled-components\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nconst StyledVisuallyHidden = styled.span`\n ${visuallyHidden}\n`;\n\nexport type VisuallyHiddenProps = ComponentPropsWithRef<\n typeof StyledVisuallyHidden\n>;\n\nexport const VisuallyHiddenStyled = (props: VisuallyHiddenProps) => (\n <StyledVisuallyHidden {...props} />\n);\n","import isPropValid from \"@emotion/is-prop-valid\";\nimport * as React from \"react\";\nimport type { VisuallyHiddenProps } from \"./VisuallyHiddenStyled\";\n\nconst mergeClassNames = (...classes: Array<string | undefined>) =>\n classes.filter(Boolean).join(\" \");\n\nexport const VisuallyHiddenTailwind = ({\n as,\n children,\n className,\n ...rest\n}: VisuallyHiddenProps) => {\n const Element = (as ?? \"span\") as React.ElementType;\n const forwardedProps =\n typeof Element === \"string\"\n ? Object.fromEntries(\n Object.entries(rest).filter(([name]) => isPropValid(name))\n )\n : rest;\n\n return React.createElement(\n Element,\n {\n ...forwardedProps,\n className: mergeClassNames(\"seeds-visually-hidden\", className),\n },\n children\n );\n};\n\nVisuallyHiddenTailwind.displayName = \"VisuallyHiddenTailwind\";\n","import { VisuallyHiddenHybrid } from \"./VisuallyHiddenHybrid\";\nimport type { VisuallyHiddenProps } from \"./VisuallyHiddenStyled\";\n\nexport type { VisuallyHiddenProps };\n\n// This wrapper component is needed for react-docgen to work.\n// It has issues with generating docs for the implementation directly.\nexport const VisuallyHidden = (props: VisuallyHiddenProps) => (\n <VisuallyHiddenHybrid {...props} />\n);\n"],"mappings":";AAAA,SAAS,6BAA6B;;;ACAtC,OAAO,YAAY;AACnB,SAAS,sBAAsB;AAY7B;AATF,IAAM,uBAAuB,OAAO;AAAA,IAChC,cAAc;AAAA;AAOX,IAAM,uBAAuB,CAAC,UACnC,oBAAC,wBAAsB,GAAG,OAAO;;;ACbnC,OAAO,iBAAiB;AACxB,YAAY,WAAW;AAGvB,IAAM,kBAAkB,IAAI,YAC1B,QAAQ,OAAO,OAAO,EAAE,KAAK,GAAG;AAE3B,IAAM,yBAAyB,CAAC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAA2B;AACzB,QAAM,UAAW,MAAM;AACvB,QAAM,iBACJ,OAAO,YAAY,WACf,OAAO;AAAA,IACL,OAAO,QAAQ,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,MAAM,YAAY,IAAI,CAAC;AAAA,EAC3D,IACA;AAEN,SAAa;AAAA,IACX;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH,WAAW,gBAAgB,yBAAyB,SAAS;AAAA,IAC/D;AAAA,IACA;AAAA,EACF;AACF;AAEA,uBAAuB,cAAc;;;AFtB1B,gBAAAA,YAAA;AAFJ,IAAM,uBAAuB,CAAC,UAA+B;AAClE,MAAI,sBAAsB,KAAK,GAAG;AAChC,WAAO,gBAAAA,KAAC,wBAAsB,GAAG,OAAO;AAAA,EAC1C;AAEA,SAAO,gBAAAA,KAAC,0BAAwB,GAAG,OAAO;AAC5C;;;AGLE,gBAAAC,YAAA;AADK,IAAM,iBAAiB,CAAC,UAC7B,gBAAAA,KAAC,wBAAsB,GAAG,OAAO;","names":["jsx","jsx"]}
package/dist/index.d.mts CHANGED
@@ -4,6 +4,7 @@ import { ComponentPropsWithRef } from 'react';
4
4
 
5
5
  declare const StyledVisuallyHidden: styled_components.StyledComponent<"span", any, {}, never>;
6
6
  type VisuallyHiddenProps = ComponentPropsWithRef<typeof StyledVisuallyHidden>;
7
+
7
8
  declare const VisuallyHidden: (props: VisuallyHiddenProps) => react_jsx_runtime.JSX.Element;
8
9
 
9
10
  export { VisuallyHidden, type VisuallyHiddenProps };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { ComponentPropsWithRef } from 'react';
4
4
 
5
5
  declare const StyledVisuallyHidden: styled_components.StyledComponent<"span", any, {}, never>;
6
6
  type VisuallyHiddenProps = ComponentPropsWithRef<typeof StyledVisuallyHidden>;
7
+
7
8
  declare const VisuallyHidden: (props: VisuallyHiddenProps) => react_jsx_runtime.JSX.Element;
8
9
 
9
10
  export { VisuallyHidden, type VisuallyHiddenProps };
package/dist/index.js CHANGED
@@ -34,16 +34,55 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
- // src/VisuallyHidden.tsx
37
+ // src/VisuallyHiddenHybrid.tsx
38
+ var import_seeds_react_system_props = require("@sproutsocial/seeds-react-system-props");
39
+
40
+ // src/VisuallyHiddenStyled.tsx
38
41
  var import_styled_components = __toESM(require("styled-components"));
39
42
  var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
40
43
  var import_jsx_runtime = require("react/jsx-runtime");
41
44
  var StyledVisuallyHidden = import_styled_components.default.span`
42
45
  ${import_seeds_react_mixins.visuallyHidden}
43
46
  `;
44
- var VisuallyHidden = (props) => {
45
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledVisuallyHidden, { ...props });
47
+ var VisuallyHiddenStyled = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledVisuallyHidden, { ...props });
48
+
49
+ // src/VisuallyHiddenTailwind.tsx
50
+ var import_is_prop_valid = __toESM(require("@emotion/is-prop-valid"));
51
+ var React = __toESM(require("react"));
52
+ var mergeClassNames = (...classes) => classes.filter(Boolean).join(" ");
53
+ var VisuallyHiddenTailwind = ({
54
+ as,
55
+ children,
56
+ className,
57
+ ...rest
58
+ }) => {
59
+ const Element = as ?? "span";
60
+ const forwardedProps = typeof Element === "string" ? Object.fromEntries(
61
+ Object.entries(rest).filter(([name]) => (0, import_is_prop_valid.default)(name))
62
+ ) : rest;
63
+ return React.createElement(
64
+ Element,
65
+ {
66
+ ...forwardedProps,
67
+ className: mergeClassNames("seeds-visually-hidden", className)
68
+ },
69
+ children
70
+ );
71
+ };
72
+ VisuallyHiddenTailwind.displayName = "VisuallyHiddenTailwind";
73
+
74
+ // src/VisuallyHiddenHybrid.tsx
75
+ var import_jsx_runtime2 = require("react/jsx-runtime");
76
+ var VisuallyHiddenHybrid = (props) => {
77
+ if ((0, import_seeds_react_system_props.needsStyledComponents)(props)) {
78
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VisuallyHiddenStyled, { ...props });
79
+ }
80
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VisuallyHiddenTailwind, { ...props });
46
81
  };
82
+
83
+ // src/VisuallyHidden.tsx
84
+ var import_jsx_runtime3 = require("react/jsx-runtime");
85
+ var VisuallyHidden = (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VisuallyHiddenHybrid, { ...props });
47
86
  // Annotate the CommonJS export names for ESM import in node:
48
87
  0 && (module.exports = {
49
88
  VisuallyHidden
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/VisuallyHidden.tsx"],"sourcesContent":["export * from \"./VisuallyHidden\";\n","import styled from \"styled-components\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nconst StyledVisuallyHidden = styled.span`\n ${visuallyHidden}\n`;\n\nexport type VisuallyHiddenProps = ComponentPropsWithRef<\n typeof StyledVisuallyHidden\n>;\n\n// This wrapper component is needed for react-docgen to work.\n// It has issues with generating docs for the styled component directly.\nexport const VisuallyHidden = (props: VisuallyHiddenProps) => {\n return <StyledVisuallyHidden {...props} />;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,+BAAmB;AACnB,gCAA+B;AActB;AAXT,IAAM,uBAAuB,yBAAAA,QAAO;AAAA,IAChC,wCAAc;AAAA;AASX,IAAM,iBAAiB,CAAC,UAA+B;AAC5D,SAAO,4CAAC,wBAAsB,GAAG,OAAO;AAC1C;","names":["styled"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/VisuallyHiddenHybrid.tsx","../src/VisuallyHiddenStyled.tsx","../src/VisuallyHiddenTailwind.tsx","../src/VisuallyHidden.tsx"],"sourcesContent":["export * from \"./VisuallyHidden\";\n","import { needsStyledComponents } from \"@sproutsocial/seeds-react-system-props\";\nimport {\n VisuallyHiddenStyled,\n type VisuallyHiddenProps,\n} from \"./VisuallyHiddenStyled\";\nimport { VisuallyHiddenTailwind } from \"./VisuallyHiddenTailwind\";\n\nexport const VisuallyHiddenHybrid = (props: VisuallyHiddenProps) => {\n if (needsStyledComponents(props)) {\n return <VisuallyHiddenStyled {...props} />;\n }\n\n return <VisuallyHiddenTailwind {...props} />;\n};\n","import styled from \"styled-components\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nconst StyledVisuallyHidden = styled.span`\n ${visuallyHidden}\n`;\n\nexport type VisuallyHiddenProps = ComponentPropsWithRef<\n typeof StyledVisuallyHidden\n>;\n\nexport const VisuallyHiddenStyled = (props: VisuallyHiddenProps) => (\n <StyledVisuallyHidden {...props} />\n);\n","import isPropValid from \"@emotion/is-prop-valid\";\nimport * as React from \"react\";\nimport type { VisuallyHiddenProps } from \"./VisuallyHiddenStyled\";\n\nconst mergeClassNames = (...classes: Array<string | undefined>) =>\n classes.filter(Boolean).join(\" \");\n\nexport const VisuallyHiddenTailwind = ({\n as,\n children,\n className,\n ...rest\n}: VisuallyHiddenProps) => {\n const Element = (as ?? \"span\") as React.ElementType;\n const forwardedProps =\n typeof Element === \"string\"\n ? Object.fromEntries(\n Object.entries(rest).filter(([name]) => isPropValid(name))\n )\n : rest;\n\n return React.createElement(\n Element,\n {\n ...forwardedProps,\n className: mergeClassNames(\"seeds-visually-hidden\", className),\n },\n children\n );\n};\n\nVisuallyHiddenTailwind.displayName = \"VisuallyHiddenTailwind\";\n","import { VisuallyHiddenHybrid } from \"./VisuallyHiddenHybrid\";\nimport type { VisuallyHiddenProps } from \"./VisuallyHiddenStyled\";\n\nexport type { VisuallyHiddenProps };\n\n// This wrapper component is needed for react-docgen to work.\n// It has issues with generating docs for the implementation directly.\nexport const VisuallyHidden = (props: VisuallyHiddenProps) => (\n <VisuallyHiddenHybrid {...props} />\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,sCAAsC;;;ACAtC,+BAAmB;AACnB,gCAA+B;AAY7B;AATF,IAAM,uBAAuB,yBAAAA,QAAO;AAAA,IAChC,wCAAc;AAAA;AAOX,IAAM,uBAAuB,CAAC,UACnC,4CAAC,wBAAsB,GAAG,OAAO;;;ACbnC,2BAAwB;AACxB,YAAuB;AAGvB,IAAM,kBAAkB,IAAI,YAC1B,QAAQ,OAAO,OAAO,EAAE,KAAK,GAAG;AAE3B,IAAM,yBAAyB,CAAC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAA2B;AACzB,QAAM,UAAW,MAAM;AACvB,QAAM,iBACJ,OAAO,YAAY,WACf,OAAO;AAAA,IACL,OAAO,QAAQ,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,UAAM,qBAAAC,SAAY,IAAI,CAAC;AAAA,EAC3D,IACA;AAEN,SAAa;AAAA,IACX;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH,WAAW,gBAAgB,yBAAyB,SAAS;AAAA,IAC/D;AAAA,IACA;AAAA,EACF;AACF;AAEA,uBAAuB,cAAc;;;AFtB1B,IAAAC,sBAAA;AAFJ,IAAM,uBAAuB,CAAC,UAA+B;AAClE,UAAI,uDAAsB,KAAK,GAAG;AAChC,WAAO,6CAAC,wBAAsB,GAAG,OAAO;AAAA,EAC1C;AAEA,SAAO,6CAAC,0BAAwB,GAAG,OAAO;AAC5C;;;AGLE,IAAAC,sBAAA;AADK,IAAM,iBAAiB,CAAC,UAC7B,6CAAC,wBAAsB,GAAG,OAAO;","names":["styled","isPropValid","import_jsx_runtime","import_jsx_runtime"]}
@@ -0,0 +1,12 @@
1
+ @layer components {
2
+ .seeds-visually-hidden {
3
+ position: absolute;
4
+ width: 1px;
5
+ height: 1px;
6
+ padding: 0;
7
+ margin: -1px;
8
+ overflow: hidden;
9
+ clip: rect(0 0 0 0);
10
+ border: 0;
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-visually-hidden",
3
- "version": "1.0.32",
3
+ "version": "1.0.34",
4
4
  "description": "Seeds React VisuallyHidden",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -8,8 +8,8 @@
8
8
  "module": "dist/esm/index.js",
9
9
  "types": "dist/index.d.ts",
10
10
  "scripts": {
11
- "build": "tsup --dts",
12
- "build:debug": "tsup --dts --metafile",
11
+ "build": "tsup --dts && cp src/visually-hidden.css dist/visually-hidden.css",
12
+ "build:debug": "tsup --dts --metafile && cp src/visually-hidden.css dist/visually-hidden.css",
13
13
  "dev": "tsup --watch --dts",
14
14
  "clean": "rm -rf .turbo dist",
15
15
  "clean:modules": "rm -rf node_modules",
@@ -17,10 +17,14 @@
17
17
  "test": "jest",
18
18
  "test:watch": "jest --watch --coverage=false"
19
19
  },
20
+ "sideEffects": [
21
+ "**/*.css"
22
+ ],
20
23
  "dependencies": {
21
- "@sproutsocial/seeds-react-theme": "^4.5.0",
24
+ "@emotion/is-prop-valid": "^1.1.0",
25
+ "@sproutsocial/seeds-react-theme": "^4.6.0",
22
26
  "@sproutsocial/seeds-react-system-props": "^3.2.1",
23
- "@sproutsocial/seeds-react-mixins": "^4.3.15"
27
+ "@sproutsocial/seeds-react-mixins": "^4.3.17"
24
28
  },
25
29
  "devDependencies": {
26
30
  "@types/react": "^18.0.0",
@@ -26,3 +26,12 @@ export const Default: Story = {
26
26
  </Text>
27
27
  ),
28
28
  };
29
+
30
+ export const TailwindUtilityOverride: Story = {
31
+ render: () => (
32
+ <VisuallyHidden className="static m-0 h-auto w-auto overflow-visible">
33
+ This text is visible because consumer utilities override the layered
34
+ component styles.
35
+ </VisuallyHidden>
36
+ ),
37
+ };
@@ -1,17 +1,10 @@
1
- import styled from "styled-components";
2
- import { visuallyHidden } from "@sproutsocial/seeds-react-mixins";
3
- import type { ComponentPropsWithRef } from "react";
1
+ import { VisuallyHiddenHybrid } from "./VisuallyHiddenHybrid";
2
+ import type { VisuallyHiddenProps } from "./VisuallyHiddenStyled";
4
3
 
5
- const StyledVisuallyHidden = styled.span`
6
- ${visuallyHidden}
7
- `;
8
-
9
- export type VisuallyHiddenProps = ComponentPropsWithRef<
10
- typeof StyledVisuallyHidden
11
- >;
4
+ export type { VisuallyHiddenProps };
12
5
 
13
6
  // This wrapper component is needed for react-docgen to work.
14
- // It has issues with generating docs for the styled component directly.
15
- export const VisuallyHidden = (props: VisuallyHiddenProps) => {
16
- return <StyledVisuallyHidden {...props} />;
17
- };
7
+ // It has issues with generating docs for the implementation directly.
8
+ export const VisuallyHidden = (props: VisuallyHiddenProps) => (
9
+ <VisuallyHiddenHybrid {...props} />
10
+ );
@@ -0,0 +1,14 @@
1
+ import { needsStyledComponents } from "@sproutsocial/seeds-react-system-props";
2
+ import {
3
+ VisuallyHiddenStyled,
4
+ type VisuallyHiddenProps,
5
+ } from "./VisuallyHiddenStyled";
6
+ import { VisuallyHiddenTailwind } from "./VisuallyHiddenTailwind";
7
+
8
+ export const VisuallyHiddenHybrid = (props: VisuallyHiddenProps) => {
9
+ if (needsStyledComponents(props)) {
10
+ return <VisuallyHiddenStyled {...props} />;
11
+ }
12
+
13
+ return <VisuallyHiddenTailwind {...props} />;
14
+ };
@@ -0,0 +1,15 @@
1
+ import styled from "styled-components";
2
+ import { visuallyHidden } from "@sproutsocial/seeds-react-mixins";
3
+ import type { ComponentPropsWithRef } from "react";
4
+
5
+ const StyledVisuallyHidden = styled.span`
6
+ ${visuallyHidden}
7
+ `;
8
+
9
+ export type VisuallyHiddenProps = ComponentPropsWithRef<
10
+ typeof StyledVisuallyHidden
11
+ >;
12
+
13
+ export const VisuallyHiddenStyled = (props: VisuallyHiddenProps) => (
14
+ <StyledVisuallyHidden {...props} />
15
+ );
@@ -0,0 +1,32 @@
1
+ import isPropValid from "@emotion/is-prop-valid";
2
+ import * as React from "react";
3
+ import type { VisuallyHiddenProps } from "./VisuallyHiddenStyled";
4
+
5
+ const mergeClassNames = (...classes: Array<string | undefined>) =>
6
+ classes.filter(Boolean).join(" ");
7
+
8
+ export const VisuallyHiddenTailwind = ({
9
+ as,
10
+ children,
11
+ className,
12
+ ...rest
13
+ }: VisuallyHiddenProps) => {
14
+ const Element = (as ?? "span") as React.ElementType;
15
+ const forwardedProps =
16
+ typeof Element === "string"
17
+ ? Object.fromEntries(
18
+ Object.entries(rest).filter(([name]) => isPropValid(name))
19
+ )
20
+ : rest;
21
+
22
+ return React.createElement(
23
+ Element,
24
+ {
25
+ ...forwardedProps,
26
+ className: mergeClassNames("seeds-visually-hidden", className),
27
+ },
28
+ children
29
+ );
30
+ };
31
+
32
+ VisuallyHiddenTailwind.displayName = "VisuallyHiddenTailwind";
@@ -0,0 +1,22 @@
1
+ /* eslint-env jest, node */
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ const cssPath = path.resolve(__dirname, "../visually-hidden.css");
6
+
7
+ describe("VisuallyHidden CSS", () => {
8
+ it("defines the semantic class in the components layer with the legacy declarations", () => {
9
+ const css = fs.readFileSync(cssPath, "utf8");
10
+
11
+ expect(css).toContain("@layer components");
12
+ expect(css).toContain(".seeds-visually-hidden");
13
+ expect(css).toContain("position: absolute;");
14
+ expect(css).toContain("width: 1px;");
15
+ expect(css).toContain("height: 1px;");
16
+ expect(css).toContain("padding: 0;");
17
+ expect(css).toContain("margin: -1px;");
18
+ expect(css).toContain("overflow: hidden;");
19
+ expect(css).toContain("clip: rect(0 0 0 0);");
20
+ expect(css).toContain("border: 0;");
21
+ });
22
+ });
@@ -1,4 +1,6 @@
1
+ import * as React from "react";
1
2
  import { render, screen } from "@sproutsocial/seeds-react-testing-library";
3
+ import styled from "styled-components";
2
4
  import { VisuallyHidden } from "../VisuallyHidden";
3
5
 
4
6
  describe("VisuallyHidden", () => {
@@ -6,10 +8,11 @@ describe("VisuallyHidden", () => {
6
8
  render(<VisuallyHidden>Hidden Content</VisuallyHidden>);
7
9
  const element = screen.getByText("Hidden Content");
8
10
  expect(element).toBeInTheDocument();
11
+ expect(element.tagName).toBe("SPAN");
9
12
  });
10
13
 
11
- it("applies visually hidden styles", () => {
12
- render(<VisuallyHidden>Hidden Content</VisuallyHidden>);
14
+ it("retains visually hidden styles on the styled fallback", () => {
15
+ render(<VisuallyHidden mt={200}>Hidden Content</VisuallyHidden>);
13
16
  const element = screen.getByText("Hidden Content");
14
17
  expect(element).toHaveStyle(`
15
18
  position: absolute;
@@ -27,4 +30,142 @@ describe("VisuallyHidden", () => {
27
30
  const element = screen.getByText("Hidden Content");
28
31
  expect(element).toHaveAttribute("aria-label", "hidden");
29
32
  });
33
+
34
+ it("renders a div with role status through the as prop", () => {
35
+ render(
36
+ <VisuallyHidden as="div" role="status" data-state="ready">
37
+ Status
38
+ </VisuallyHidden>
39
+ );
40
+ const element = screen.getByRole("status");
41
+ expect(element.tagName).toBe("DIV");
42
+ expect(element).toHaveAttribute("data-state", "ready");
43
+ expect(element).not.toHaveAttribute("as");
44
+ });
45
+
46
+ it("forwards className, id, aria, and events", () => {
47
+ const onClick = jest.fn();
48
+ render(
49
+ <VisuallyHidden
50
+ className="consumer-class"
51
+ id="hidden-id"
52
+ aria-label="hidden label"
53
+ onClick={onClick}
54
+ >
55
+ Hidden Content
56
+ </VisuallyHidden>
57
+ );
58
+ const element = screen.getByText("Hidden Content");
59
+ expect(element).toHaveClass("consumer-class");
60
+ expect(element).toHaveAttribute("id", "hidden-id");
61
+ expect(element).toHaveAttribute("aria-label", "hidden label");
62
+ element.click();
63
+ expect(onClick).toHaveBeenCalledTimes(1);
64
+ });
65
+
66
+ it("accepts but does not leak the intentionally loose invalid prop", () => {
67
+ const error = jest.spyOn(console, "error").mockImplementation(() => {});
68
+ render(
69
+ <VisuallyHidden invalidProp="invalid">Hidden Content</VisuallyHidden>
70
+ );
71
+ expect(screen.getByText("Hidden Content")).not.toHaveAttribute(
72
+ "invalidProp"
73
+ );
74
+ expect(error).not.toHaveBeenCalled();
75
+ error.mockRestore();
76
+ });
77
+
78
+ it("preserves the current non-forwarded ref behavior", () => {
79
+ const ref = React.createRef<HTMLSpanElement>();
80
+ const originalError = console.error;
81
+ const expectedWarning = "Function components cannot be given refs";
82
+ const error = jest
83
+ .spyOn(console, "error")
84
+ .mockImplementation((...args: Parameters<typeof console.error>) => {
85
+ if (typeof args[0] === "string" && args[0].includes(expectedWarning)) {
86
+ return;
87
+ }
88
+
89
+ originalError(...args);
90
+ });
91
+
92
+ render(<VisuallyHidden ref={ref}>Hidden Content</VisuallyHidden>);
93
+
94
+ expect(ref.current).toBeNull();
95
+ expect(
96
+ error.mock.calls.filter(
97
+ ([message]) =>
98
+ typeof message === "string" && message.includes(expectedWarning)
99
+ )
100
+ ).toHaveLength(1);
101
+ expect(
102
+ error.mock.calls.filter(
103
+ ([message]) =>
104
+ typeof message !== "string" || !message.includes(expectedWarning)
105
+ )
106
+ ).toHaveLength(0);
107
+ error.mockRestore();
108
+ });
109
+
110
+ it("passes arbitrary props and className to a custom component through as", () => {
111
+ const CustomComponent = ({
112
+ arbitraryProp,
113
+ className,
114
+ children,
115
+ }: {
116
+ arbitraryProp?: string;
117
+ className?: string;
118
+ children?: React.ReactNode;
119
+ }) => (
120
+ <span data-arbitrary-prop={arbitraryProp} className={className}>
121
+ {children}
122
+ </span>
123
+ );
124
+
125
+ render(
126
+ <VisuallyHidden
127
+ as={CustomComponent}
128
+ arbitraryProp="preserved"
129
+ className="consumer-class"
130
+ >
131
+ Hidden Content
132
+ </VisuallyHidden>
133
+ );
134
+
135
+ const element = screen.getByText("Hidden Content");
136
+ expect(element).toHaveAttribute("data-arbitrary-prop", "preserved");
137
+ expect(element).toHaveClass("consumer-class");
138
+ });
139
+
140
+ it("uses the Tailwind path by default", () => {
141
+ render(<VisuallyHidden>Hidden Content</VisuallyHidden>);
142
+ expect(screen.getByText("Hidden Content")).toHaveClass(
143
+ "seeds-visually-hidden"
144
+ );
145
+ });
146
+
147
+ it("merges a plain consumer className on the Tailwind path", () => {
148
+ render(<VisuallyHidden className="static">Hidden Content</VisuallyHidden>);
149
+ expect(screen.getByText("Hidden Content")).toHaveClass(
150
+ "seeds-visually-hidden",
151
+ "static"
152
+ );
153
+ });
154
+
155
+ it("routes styled-system props to the styled-components fallback", () => {
156
+ render(<VisuallyHidden mt={200}>Hidden Content</VisuallyHidden>);
157
+ expect(screen.getByText("Hidden Content")).not.toHaveClass(
158
+ "seeds-visually-hidden"
159
+ );
160
+ });
161
+
162
+ it("routes a styled extension to the styled-components fallback", () => {
163
+ const Extended = styled(VisuallyHidden)`
164
+ opacity: 0.5;
165
+ `;
166
+ render(<Extended>Hidden Content</Extended>);
167
+ const element = screen.getByText("Hidden Content");
168
+ expect(element).not.toHaveClass("seeds-visually-hidden");
169
+ expect(element).toHaveStyleRule("opacity", "0.5");
170
+ });
30
171
  });
@@ -9,6 +9,9 @@ function VisuallyHiddenTypes() {
9
9
  {/* This is allowed _for now_ since style.span component can accept anything */}
10
10
  {/* In a future major release, consider making VisuallyHidden's prop more restrictive */}
11
11
  <VisuallyHidden invalidProp="invalid">Content</VisuallyHidden>
12
+ <VisuallyHidden as="div" role="status">
13
+ Status
14
+ </VisuallyHidden>
12
15
  </>
13
16
  );
14
17
  }
@@ -0,0 +1,12 @@
1
+ @layer components {
2
+ .seeds-visually-hidden {
3
+ position: absolute;
4
+ width: 1px;
5
+ height: 1px;
6
+ padding: 0;
7
+ margin: -1px;
8
+ overflow: hidden;
9
+ clip: rect(0 0 0 0);
10
+ border: 0;
11
+ }
12
+ }