Tailwind: extend theme with a custom _responsive_ spacing - tailwind-css

I'm new to Tailwind. I'm currently extending the list of components/utilities with a lot of similary purposed classes:
/* components.css / utilities.css (doesn't matter in this context) */
.pt-section {
#apply pt-16 lg:pt-20;
}
.mt-section {
#apply mt-16 lg:mt-20;
}
.-mt-section {
#apply -mt-16 lg:-mt-20;
}
.top-section {
#apply top-16 lg:top-20;
}
.-top-section {
#apply -top-16 lg:-top-20;
}
/*
Also: pb-section, mb-section, -mb-section bottom-section, -bottom-section, gap-section...
*/
The purpose of this utils is to be able to quickly make e.g. grid gap "same width as the section margin-bottom height" – pretty usable stuff.
I see that it would be much nicer to define a single responsive value in the config. I already do that for non-responsive constants describing body and header heights:
// tailwind.config.js
theme: {
extend: {
spacing: {
"13": "3.25rem",
"18": "4.5rem",
"header": "5rem",
"header-1": "calc(5rem - 1px)",
"body": "calc(100vh - 5rem)",
"sectionT": ??? should be responsive !!!
"sectionB": ??? should be responsive !!!
}
}
},
In this way TW would auto-derive all that I need. I would get mt-sectionT, gap-sectionB etc. classes automatically.
Is there a way to declare custom responsive (breakpoint-depending) values in the tailwind.config.js? Without writing a custom plugin, I mean.
It sounds like a common case, yet I can't find the answer in the docs (checked https://tailwindcss.com/docs/theme#customizing-the-default-theme and other pages) or on StackOverflow.

Related

How to add custom padding-right to tailwind?

I want a larger value for padding right than tailwind currently offers (pr-96 which is around 24rem) I’d like something around 40rem for my project.
Following documentation, I tried, but I cannot run npm run build without an error.
// tailwind.config.js
module.exports = {
theme: {
padding: {
xxl: 'padding-right: 40rem',
}
}
}
’’’
Error:the xx class does not exist, but xx does.
If I remove the code I added above, error goes away.
You don't need to specify the property padding-right in your creation of the xxl utility only the value and unit, this line:
xxl: 'padding-right: 40rem'
should be:
xxl: '40rem'
This will give you an xxl size of 40rem for all padding classes p-xxl, pr-xxl, pl-xxl, etc...
However, you should only use padding as a direct child of theme when you want to replace all padding utilities. If you wanted to just add this size to the existing padding sizes you should use extend inside theme like this:
// tailwind.config.js
module.exports = {
theme: {
extend: {
padding: {
xxl: '40rem'
}
}
}
}

Set min-width using tailwind spacing units without global config?

Is there a way to apply spacified spacing units from tailwind.config.js to e.g. min-width other than by global config? That's as far I can see the only way but would mean to duplicate all (necessary) spacing units which is from my pov a potential error source as each relevant space must get duplicated then.
// Dummy code explaining what I want to achieve
.myButton{
#apply bg-red-100 w-auto;
min-width: #apply w-11 // Not working just for theoretical explanation
}
I'll provide every known option for me
1. Provide value inside config
module.exports = {
theme: {
extend: {
minWidth: {
11: '2.75rem'
}
}
}
}
This will generate min-w-11 class with min-width: 2.75rem. But what if for some reason Tailwind change 11 value to let say 197px, how can we sync it? Well every option has access to default theme options like
module.exports = {
theme: {
extend: {
minWidth: theme => ({
11: theme('spacing[11]')
})
}
}
}
This time min-w-11 will set min-width: 197px;. It referenced here
2. Use theme() directive
It is works inside CSS files like - no need to set config unless you need new value which is not present in default theme
.myButton{
#apply bg-red-100 w-auto;
min-width: theme('spacing[11]'); // 2.75rem
}
More information about theme() directive here
3. With a JIT mode enabled - requires Tailwind version 2.1 or higher
module.exports = {
mode: 'jit'
}
This on its own will generate CSS properties on the fly
<button class="myButton min-w-[2.75rem]">My Button</button>
<button class="myButton min-w-[197px]">My Button</button>
generated properties are
.min-w-\[2\.75rem\] {
min-width: 2.75rem;
}
.min-w-\[197px\] {
min-width: 197px;
}
You can read about JIT here
Please let me know if my answer is not what are you looking for as I may misunderstood question
min-w-[theme('spacing[11]')] also works, tested in Tailwind v3.

React-Admin - adding material ui theme overrides specific css selector as a global

In React-Admin, I'm trying to apply certain css code inside my Material UI theme as a global attribute. Right when I'm creating my theme, I've been added those lines inside my overrides:
overrides: {
"#global": {
"[class*='RaLayout-content']": {
overflow: "auto !important",
maxWidth: "100vw !important",
},
},
In the entire admin I have many classes like: RaLayout-content-4, RaLayout-content-221, RaLayout-content-31, which are generated by the React-Admin, and I want to apply those css lines in every element that contains the RaLayout-content class.
Because of class names minimization of Heroku deploy, I cannot write those css lines in my index.css cause they'll not apply after the minimization.
Here's how I implemented them before, inside my index.css file (which is working only in development mode):
[class*="RaLayout-content"] {
overflow: auto !important;
max-width: 100vw !important;
}
Notice: I've been also trying to add the MuiCssBaseLine with no success.
Thanks in advance!
I think that you can override the theme with
overrides: {
RaLayout: {
content: {
// your overrides
},
},
...
}

Angular: How to add global CSS (e.g. to the body), but only for one specific page?

How can I add separate CSS for one page in Angular?
This is the CSS I need, as per How to remove the URL from the printing page?:
#media print{
#page{
margin:0;
}
body{
margin:30px;
}
}
But putting CSS into the component with ::ng-deep or ViewEncapsulation.None won't help here, because when navigating away from a page, the CSS of the page isn't deleted.
I've added a Stackblitz, which explains the problem clearly.
I've come up with a potential solution, but it doesn't work:
encapsulation: ViewEncapsulation.None
...
constructor(private renderer: Renderer2) {
this.renderer.addClass(document.body, 'special-print');
}
ngOnDestroy() {
this.renderer.removeClass(document.body, 'special-print');
}
....
....
....
#media print{
#page{
margin:0;
}
body.special-print{
margin:30px;
}
}
Why it doesn't work:
While it would help with <body> CSS, it won't help with #page CSS. Perhaps the question would be better summarized as "How to add global CSS, but remove it when we leave the page?".
Solved!
We print the <style> block directly into the component's HTML, and therefore when the component gets removed, our <style> block gets removed too. (Normally this wouldn't work, but thanks to DomSanitizer.bypassSecurityTrustHtml, Angular won't break our code when running optimizations.)
Here's a StackBlitz.
First, create a new component to handle the work:
component.ts: (This is all we need. We don't need an HTML or style.css file.)
//Inside your local component, place this HTML
//<app-local-css [style]="'body{background:green !important;}'"></app-local-css>
// OR
//<app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>
#Component({
selector: "app-local-css",
template: '<span style="display:none" [innerHTML]="this.safeString"></span>'
})
export class LocalCSSComponent implements OnInit {
constructor(protected sanitizer: DomSanitizer) {}
#Input() scriptURL?: string;
#Input() style?: string;
safeString: SafeHtml;
ngOnInit() {
if (this.scriptURL) {
let string = '<link rel="stylesheet" type="text/css" href="' + this.scriptURL + '">';
this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
} else if (this.style) {
let string = '<style type="text/css">' + this.style + "</style>";
this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
}
}
}
And then use it like this:
mySample.component.html:
<app-local-css [style]="'body{background:green !important;}'"></app-local-css>
// OR
<app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>
Angular is doing client-side rendering, which is bad news, because you do not have separate pages. You have several possible solutions though:
1. Separate page
You can create another page with or without Angular, which includes the CSS you need and load that page. In the most simplistic approach to achieve this, the other page would have a different URL. If having a different URL is not to your liking, then you could hide your page's content and show the other page inside an iframe. It would admittedly be a hacky solution, but it is a solution.
2. Client-side CSS rendering
Instead of just loading the CSS, you could have a component which would control global CSS rules, matched by your view's name. You would have a template value rendered to a property, like:
#media print{
#page{
margin:0;
}
body{
margin:30px;
}
}
And when you visit the page where this needs to be activated, you would simply initialize a property with a style HTML element that was generated based on the template and added to head. Once you leave the given view, your component would detect that event and would remove() that element. If you choose this solution, then it would be wise to make sure that you are supporting this on more general terms, so that if some new views will have their custom global CSS, then they would be easy to integrate into your project in the future.
3. body classes
You could add/remove some custom-print or whatever class to/from body whenever the style is to be changed. This way you could add the CSS exactly once to your HTML and change the rules accordingly, like:
body.custom-print {
margin: 30px;
}
This would be a neat solution, but the problem in your case is that you have a #page rule as well and I'm not sure how you could make that dependant on body classes or some other HTML attributes. I would conduct quite a few experiments about this if I were you.
4. Iframe staging
You could avoid having that CSS in your main page, but would have a hidden iframe where you would have the CSS and would just copy the content into the CSS and once that's loaded, print that.
Don't change the whole body from apple. Instead, there are a few changes to make.
In the app component, hold a boolean for whether or not you are on apple, and use ngClass for class defined in scss.
Track which route you are on in appComponent, and set isApple accordingly
Add a div around all your html, for container to take full size
Add global html, body setting height to 100% so you see color everywhere
Remove body overriding in apple
so,
appComponent.ts:
isApple: Boolean;
constructor(router: Router) {
router.events.subscribe(v => {
if (v instanceof NavigationEnd) {
this.isApple = v.url === "/apple";
}
});
}
appComponent.html:
<div [ngClass]="{'red':isApple}" class="container">
<p>
There are two components: Apple and Banana. Switching between them will show
the problem.
</p>
<router-outlet></router-outlet>
</div>
appComponent.scss
.red {
background-color: red;
}
.container {
height: 100%;
}
apple.component.scss (remove body)
/*Sample "global" CSS, that affects something outside the current component.*/
::ng-deep {
#media print {
#page {
margin: 0;
}
}
}
styles.scss (global)
html, body {
height: 100%;
}
You can see this altogether at this Stackblitz link
You can add different css files in the component (for instance, app-task.component.ts):
#Component({
selector: 'app-task',
templateUrl: './app-task.component.html',
styleUrls: ['./app-task.component.scss', './styles2.scss', './styles3.scss']
})
In this example, the style files are in the same folder that the component, but this is not the best option: you have to put the files in assets, for example. Also, be careful with the thread of the styles, since the first one you put will be put before the second (obviously).

vue binding value based on media query

carousel-3d(:display="3", :width="150", :height="150")
I want to set the attribute bindings based on a media query
e.g.
display should become 5 when screen width > 960px
You could try binding the display value to a component property:
<carousel-3d :display="display">
...and then update that property on window resize:
...
data() {
return {
display: 3
}
},
methods: {
onResize() {
if (window.innerWidth > 960) {
this.display = 5
} else {
this.display = 3
}
}
},
created() {
window.addEventListener('resize', this.onResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.onResize)
},
...
I struggled to find any solutions with Vuejs alone myself or if at all that are there, are already too complex. Involves a lot of unnecessary work where things can be done neatly and in CSS.
That solution is styled components or for a matter of fact any CSS in JS solutions to handle such things with ease.
For example in styled components your styles are written inside ES6 template literal.
import styled from 'styled-components';
import breakpoint from '../utils/breakpoint.js';
const YourStyledComponent = styled.div`
width: calc(100% - 30px);
#media (min-width: ${breakpoint.SM}px) {
// Your media styles
}
`;
Then inside your Vue component use it as a normal component. You can pass it props as well. Do read more about it - Vue-styled-components.
Doing this way you are using just CSS for styling everything. It's a must for front-end development considering the performance. I think it's a long time since we stopped adding eventListeners to Javascript related to styling or handling layout.

Resources