I am using the awesome "Styled-Components"
but I am now using another package that wraps an element inside it so I can't push my StyledComponents there as I don't want to change his package.
I saw glamor has a nice trick.
Is that supported with StyledComponents?
import { css } from 'glamor';
let rule = css({
color: 'red',
})
<div {...rule}>
zomg
</div>
If you think about why I need it, here is an example:
this is an external package I'm using:
External = props => (
<div>
<input style={props.inputStyle} className={props.inputClass} />
</div>
);
so you can see I need to pass in a json style or className
so Glamor will work here, but I dont want to use it just for this scenario.
I'm already enjoying StyledComponent
Thanks
If I understood your query, you can define css rules to a component, like this
import styled from 'styled-components'
const Wrapper = styled.div`
color: 'red';
font-weight: bold;
background-color: ${ props => props.color === 'primary' ? 'green' : 'red' }
`
export const Component = () => {
<Wrapper color='primary'>
I have a red text, and bold font-weight.
</Wrapper>
}
Related
The bounty expires in 5 days. Answers to this question are eligible for a +50 reputation bounty.
andrilla wants to draw more attention to this question:
I would love to see some ideas of what might be causing this and how I might fix it. Ideally I'd love an actual answer, but just some good ideas of what I might try would be super helpful.
#next/font
Uses Next.js with TypeScript and Tailwind CSS
This is my first time using the new #next/font package. I followed Next.js' tutorial, and it was easy to set up. I'm using both Inter and a custom local typeface called App Takeoff. To actually use both of these typefaces, I'm using Tailwind CSS, where Inter is connected to font-sans and App Takeoff is connected to font-display.
Everything works except in one spot
I have done plenty of testing between files, and for some reason both typefaces work everywhere except my Modal component.
Example
index.tsx
modal.tsx via index.tsx
As you can see, the typefaces work just fine when they aren't inside the modal, but as soon as they're in the modal they don't work.
Here's some relevant code:
// app.tsx
import '#/styles/globals.css'
import type { AppProps } from 'next/app'
import { Inter } from '#next/font/google'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter'
})
import localFont from '#next/font/local'
const appTakeoff = localFont({
src: [
{
path: '../fonts/app-takeoff/regular.otf',
weight: '400',
style: 'normal'
},
{
path: '../fonts/app-takeoff/regular.eot',
weight: '400',
style: 'normal'
},
{
path: '../fonts/app-takeoff/regular.woff2',
weight: '400',
style: 'normal'
},
{
path: '../fonts/app-takeoff/regular.woff',
weight: '400',
style: 'normal'
},
{
path: '../fonts/app-takeoff/regular.ttf',
weight: '400',
style: 'normal'
}
],
variable: '--font-app-takeoff'
})
const App = ({ Component, pageProps }: AppProps) => {
return (
<div className={`${inter.variable} font-sans ${appTakeoff.variable}`}>
<Component {...pageProps} />
</div>
)
}
export default App
// modal.tsx
import type { FunctionComponent } from 'react'
import type { Modal as ModalProps } from '#/typings/components'
import React, { useState } from 'react'
import { Fragment } from 'react'
import { Transition, Dialog } from '#headlessui/react'
const Modal: FunctionComponent<ModalProps> = ({ trigger, place = 'bottom', className, addClass, children }) => {
const [isOpen, setIsOpen] = useState(false),
openModal = () => setIsOpen(true),
closeModal = () => setIsOpen(false)
const Trigger = () => React.cloneElement(trigger, { onClick: openModal })
const enterFrom = place === 'center'
? '-translate-y-[calc(50%-12rem)]'
: 'translate-y-full sm:-translate-y-[calc(50%-12rem)]'
const mainPosition = place === 'center'
? '-translate-y-1/2'
: 'translate-y-0 sm:-translate-y-1/2'
const leaveTo = place === 'center'
? '-translate-y-[calc(50%+8rem)]'
: 'translate-y-full sm:-translate-y-[calc(50%+8rem)]'
return (
<>
<Trigger />
<Dialog open={isOpen} onClose={closeModal} className='z-50'>
{/* Backdrop */}
<div className='fixed inset-0 bg-zinc-200/50 dark:bg-zinc-900/50 backdrop-blur-sm cursor-pointer' aria-hidden='true' />
<Dialog.Panel
className={`
${className || `
fixed left-1/2
${
place === 'center'
? 'top-1/2 rounded-2xl'
: 'bottom-0 sm:bottom-auto sm:top-1/2 rounded-t-2xl xs:rounded-b-2xl'
}
bg-zinc-50 dark:bg-zinc-900
w-min
-translate-x-1/2
overflow-hidden
px-2 xs:px-6
shadow-3xl shadow-primary-400/10
`}
${addClass || ''}
`}
>
{children}
</Dialog.Panel>
<button
onClick={closeModal}
className='
fixed top-4 right-4
bg-primary-600 hover:bg-primary-400
rounded-full
h-7 w-7 desktop:hover:w-20
overflow-x-hidden
transition-[background-color_width] duration-300 ease-in-out
group/button
'
aria-role='button'
>
Close
</button>
</Dialog>
</>
)
}
export default Modal
I hope this information helps. Let me know if there's anything else that would be helpful to know.
Helpful Update
Thank you Jonathan Wieben for explanation of why this isn't working (See Explanation). The issue simply has to do with the scope of the applied styles, and Headless UI's usage of the React Portal component. If anyone has some ideas of how I can either change where the Portal is rendered or change the scope of the styles, that would be super helpful. Jonathan Wieben pointed out a way to do this, however—from my testing—it doesn't work with Tailwind CSS.
The Dialog component you are using renders in a portal (see here).
you typically want to render them as a sibling to the root-most node of your React application. That way you can rely on natural DOM ordering to ensure that their content is rendered on top of your existing application UI.
You can confirm this by inspecting your modal DOM element in your browser and seeing if it is indeed placed outside the div wrapper from your App component (I suspect it is).
If so, this is the explanation for why the modal content does not render with the expected font: It is rendered outside the component that defines the font.
To get around this, you could define your font on a higher level, e.g. in your head like described here: Next docs.
Can I use tailwind classes ( like colors ) into the styled-components ?
I want to use some classes instead of CSS styles to style my components
this is the way add class in styled-components:
const Button = styled.button.attrs(props => ({
className: "small",
}))`
/* other styles */
`;
so unlike styles, attrs className is only one single string, and I want to add classes for size, color, display and etc.
I have to concat them all every time, is there a better way ?
You can use macro, I suggest trying twin.macro:
import tw, { styled } from 'twin.macro'
const Input = styled.input`
color: purple;
${tw`border rounded`}
${({ hasHover }) => hasHover && tw`hover:border-black`}
`
const Component = () => <Input hasHover />
New to react and styled-components and have probably got myself in a muddle through not understanding how it all works.
Let's start from the top.
I have a simple page (App.js) that renders two components "Knobs".
I want to pass each 'Knob' one or more properties so it can calculate its size and other relevant instance props. In the example below, one know is 200px in size, and it's sister is a 100px.
import React from 'react';
import Knob from './components/knob.js'
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
hello world
<Knob size={200} />
<Knob size={100} />
</header>
</div>
);
}
export default App;
So far so good.
Now inside the Knob component, I do all my transformations and ultimately have a scaled Knob.
The knob is a svg based component (abbreviated below but still long, sorry).
So - the good news is that it all works! But I know I am approaching this wrong.
In order to get it to work and use the this.state.size to calculate the appropriate font size for the component , I had to move the styled-component object into the class...and create an empty declaration outside the class (Styles).
So - my ask is two-fold:
I think my approach is philosophically damaged...and would love experts here to unscramble my brain.
How would you edit the code to make it not just work, but work right!
a) It seems to me that the entire Styles declaration belongs outside the class.
b) No idea why I have to reference this.state.xxxx twice
c) I think I am also mixing up the use of props and state.
Other than that it's perfect (:. But -- as you see from the screenshot below...it actually works.
Ugh.
import React from 'react'
import { Knob, Pointer, Value, Scale, Arc } from 'rc-knob'
import styled from 'styled-components';
// this is my weird hack to get things working. Declare Styles outside of the class.
var Styles = {}
export default class MyKnob extends React.Component {
constructor(props) {
super(props)
this.state = {
size: props.size,
value: props.value,
radius: (props.value/2).toString(),
fontSize: (props.size * .2)
}
//Now define styles inside the class and i can use the fontsize that is derived from the size passed by the parent component!
Styles = styled.div`
.vpotText {
fill: green;
font-size: ${this.state.fontSize+'px'};
}
`
}
// no idea why I need this block....but without it I get a whole bunch of
// error TS2339: Property 'value' does not exist on type 'Readonly<{}>'.
state = {
value: 50,
size: 100,
radius: '50',
fontSize: 12
}
static defaultProps = { value: 50, size: 100};
render(){
const customScaleTick = ({}) //abbreviated for readability.
return (
<Styles>
<Knob size={this.state.size}
angleOffset={220}
angleRange={280}
steps={10}
min={0}
max={100}
// note use of this.state.value to set parameters that affect the sizing/display of the component
value={this.state.value}
onChange={value => console.log(value)}
>
<Scale steps={10} tickWidth={1} tickHeight={2} radius={(this.state.size/2)*0.84} color='grey' />
<Arc arcWidth={2} color="#4eccff" background="#141a1e" radius = {(this.state.size/2)*0.76} />
<defs>
{/* GRADIENT DEFINITIONS REMOVED FOR READABILITY */}
</defs>
{/* NOTE: EXTENSIVE USE OF this.state.size TO ENSURE ALL PARTS OF THE COMPONENT ARE SCALED NICELY */}
<circle cx={this.state.size/2} cy={this.state.size/2} rx={(this.state.size/2)*0.8} fill = "url(#grad-dial-soft-shadow)" />
<ellipse cx={this.state.size/2} cy={(this.state.size/2)+2} rx={(this.state.size/2)*0.7} ry={(this.state.size/2)*0.7} fill='#141a1e' opacity='0.15' ></ellipse>
<circle cx={this.state.size/2} cy={this.state.size/2} r={(this.state.size/2)*0.7} fill = "url(#grad-dial-base)" stroke='#242a2e' strokeWidth='1.5'/>
<circle cx={this.state.size/2} cy={this.state.size/2} r={(this.state.size/2)*0.64} fill = 'transparent' stroke='url(#grad-dial-highlight)' strokeWidth='1.5'/>
<Pointer width={(this.state.size/2)*0.05} radius={(this.state.size/2)*0.47} type="circle" color='#4eccff' />
{/* THIS IS THE TRICKY ONE! */}
{/* IN ORDER TO GET THE FONT SIZE RIGHT ON THIS ELEMENT (svg) I NEED THE STYLE */}
<Value
marginBottom={(this.state.size-(this.state.fontSize)/2)/2}
className="vpotText"
/>
</Knob>
</Styles>
)}
}
here's a pic of the output:
a) This is how we use props variables in styled components:
const Styles = styled.div`
.vpotText {
fill: green;
font-size: ${props => props.fontSize}px;
};
`;
b) That way you won't need to call the state twice
render(){
return(
<Styles fontSize={this.state.fontSize}>
...
</Styles>
)}
styled-components are really cool once you get the hang of them.
d) Also, I suggest you make value it's own component instead of wrapping it and calling the class.
const StyledValue = styled(Value)`
fill: green;
font-size: ${props => props.fontSize}px;
`;
This looks like it would be a good use case for passing a prop into a Styled Component. It would look something like this:
var Styles = styled.div`
.vpotText {
fill: green;
font-size: ${props => props.size};
}
`
<Styles size={someSize}>
...
</Styles>
You can find the documentation here:
https://styled-components.com/docs/basics#passed-props
I was using this Link from #material-ui/core/Link in my TypeScript code and it worked perfectly:
<Link href="#" variant="body2">
Forgot?
</Link>
However, I am trying to switch to styled-components placed in another file. Now, I am trying to use this (eg: https://styled-components.com/docs/basics):
const Link = ({ className, children}) => (
<a className={className}>
{children}
</a>
);
export const StyledLink = styled(Link)`
href: #;
variant: body2;
`;
along with:
<StyledLink>Forgot?</StyledLink>
But I keep getting errors on className and children that Binding element 'children' implicitly has an 'any' type.ts(7031but even if I add any, It doesn't work.
What is the correct way to use styled-components in this case? Or any other css-in-js alternative?
This code works, and gets no warnings from the typescript compiler
import styled from "styled-components";
const Link = ({
className,
children
}: {
readonly className: string;
readonly children: React.ReactElement;
}) => (
<a href="/" className={className}>
{children}
</a>
);
export const StyledLink = styled(Link)`
href: #;
variant: body2;
color: red;
`;
function App() {
return (
<StyledLink className="classic">
<div>Forgot?</div>
</StyledLink>
);
}
You should make your Link component just like below:
// Link.js
import { Link } from '#material-ui/core/Link';
import styled from 'styled-components';
export default styled(Link)`
display: block;
color: #F51963;
text-decoration: none;
`;
You can use the <Link> on top of other divs or buttons for example
<Link><button className="Btn">Forgot?</button></Link>
you can then do two things to style the button / divs
<Link><button className="Btn" style={{backgroundColor: 'red'}}>Forgot?</button></Link>
or the second is import a separate css file
import classes from './ComponentName.module.css'
and then give a style
<Link><button className={classes.Btn}>Forgot?</button></Link>
I would like to set global style for the react-select. For my understanding I can do 2 ways:
Using className and classNamePrefix and then target elements using CSS.
PROS: I can use the same style everywhere
CONS: Every new component must use exactly the same className and classNamePrefix
Example:
className='react-select-container'
classNamePrefix="react-select"
Result:
<div class="react-select-container">
<div class="react-select__control">
<div class="react-select__value-container">...</div>
<div class="react-select__indicators">...</div>
</div>
<div class="react-select__menu">
<div class="react-select__menu-list">
<div class="react-select__option">...</div>
</div>
</div>
</div>
Create external javascript file with "Provided Styles and State"
PROS: more flexible then CSS
CONS: Every new component must use style property using imported external file.
Example:
const customStyles = {
option: (provided, state) => ({
...provided,
borderBottom: '1px dotted pink',
color: state.isSelected ? 'red' : 'blue',
padding: 20,
}),
control: () => ({
// none of react-select's styles are passed to <Control />
width: 200,
}),
singleValue: (provided, state) => {
const opacity = state.isDisabled ? 0.5 : 1;
const transition = 'opacity 300ms';
return { ...provided, opacity, transition };
}
}
const App = () => (
<Select
styles={customStyles}
options={...}
/>
);
What is the best way to style multiple react-select components? Will be possible to set style globally and every new react-select component use that style automatically?
One way to do it is to create your own select component like CustomSelect that you import instead of react-select where you set for one the custom style or theme like:
import React, { Component } from 'react'
import Select from 'react-select'
class CustomSelect extends Component {
render() {
const styles = {
...
// what ever you need
}
return <Select styles={styles} {...this.props} />
}
}
export default CustomSelect
I can't really tell if it's the best way or not but I've tried both of it and in a big project with many select it's the easiest way to maintain / modify it. Plus it's really convenient if at some point you need to have custom components.