I'm trying to add a new variant called not-first to the Tailwind toolbelt, but when I try to use it, it doesn't work.
tailwind.config.js:
const plugin = require('tailwindcss/plugin');
module.exports = {
mode: 'jit',
purge: ['./src/**/*.{js,jsx}', './public/index.html'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [
plugin(({ addVariant, e }) => {
addVariant('not-first', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
const element = e(`not-first${separator}${className}`);
return `.${element} > :not(:first-child)`;
});
});
}),
],
}
Usage:
<li className={`not-first:bg-blue`}>{item.name}</li>
What am I doing wrong?
It looks like you're applying the :not(:first-child) to the child of the element you're targeting with the not-first: pseudo-class.
:first-child should be applied to the child element itself (see: https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child).
To fix this, you could try changing
return `.${element} > :not(:first-child)`;
to
return `.${element}:not(:first-child)`;
Related
In the following code, I am trying to dynamically update a template with attribute data that is not available until the first useEffect call. In the code below, the first render of course has emoty attributes and displays defaults. I can trace the code and see the template get updated, but after the useInnerBlocksProps is called with the revised template, the data is still not rendered. what am I missing?
export default function Edit(props) {
const { PanelBody, PanelRow } = wp.components;
const { attributes, setAttributes } = props;
useEffect(() => {
setAttributes({'name': 'Edward Alan Thompson'});
setAttributes({'birth_date': '1 jan 1900'});
setAttributes({'death_date': '31 Dec 1990'});
setAttributes({'imageUrl': 'http://wordpressdevel.local/wp-content/uploads/2022/07/1.png'});
}, []);
const blockProps = useBlockProps( {
style:{minHeight: 40, width: '100%', border: "1px dotted red"}
});
const innerBlocksProps = useInnerBlocksProps(
{orientation: "horizontal"},
{template: [
[ 'core/columns', { columns: 2 }, [
[ 'core/column', {width: "20%"}, [
[ 'core/image', { url: attributes.imageUrl??''} ]
] ],
[ 'core/column', {}, [
[ 'core/heading', { content: attributes.name??''} ],
[ 'core/paragraph', { content: 'b. ' + attributes.birth_date??''} ],
[ 'core/paragraph', { content: 'd. ' + attributes.death_date??''} ]
] ],
]
]
]}
);
return (
<div >
<InspectorControls>
<PanelBody title="General" initialOpen={true}>
<PanelRow>
</PanelRow>
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<div {...innerBlocksProps}></div></div>
</div>
);
}
I verified that the last render call does have the proper template definition, its just not what is displayed on the screen.
Looking at your code, the issue seems to be with how useInnerBlocksProps() is called: the first parameter expects blockProps followed by the innerBlocks content, eg:
edit.js
import { useEffect } from '#wordpress/element';
import { useInnerBlocksProps, useBlockProps } from '#wordpress/block-editor';
export default function Edit({ attributes, setAttributes }) {
// Nb. Removed code not related to issue for clarity
useEffect(() => {
// Call setAttributes once
setAttributes({
name: "Edward Alan Thompson",
birth_date: "1 jan 1900",
death_date: "31 Dec 1990",
image_url: "http://wordpressdevel.local/wp-content/uploads/2022/07/1.png",
});
}, []);
const blockProps = useBlockProps({
style: { minHeight: 40, width: '100%', border: "1px dotted red" }
});
const TEMPLATE = [
['core/columns', {}, [ // Number of columns not needed
['core/column', { width: '20%' }, [
['core/image', { url: attributes.image_url ?? '' }]],
],
['core/column', {}, [
['core/heading', { content: attributes.name ?? '' }],
['core/paragraph', { content: 'b. ' + attributes.birth_date ?? '' }],
['core/paragraph', { content: 'd. ' + attributes.death_date ?? '' }]
]]
]]]
const { children, ...innerBlocksProps } = useInnerBlocksProps(blockProps,
{
orientation: "horizontal",
template: TEMPLATE
}
);
return (
<div {...innerBlocksProps}>
{children}
</div>
);
}
Ref: Inner Blocks: Using a react hook
I came to this conclusion after testing your template definition by itself using <InnerBlocks .../> then checked what useInnerBlocksProps() expected.
I would like to add translate-z-[] utility class to my tailwindcss classes so I could use parallax scrolling effects with perspective and translateZ, is there a way to generate these classes (add something like --tw-translate-z variable at the end on tailwinds transform class)?
Yes, you can create plugins for custom utilities
You config should have something like this
const plugin = require('tailwindcss/plugin');
module.exports = {
theme: {},
plugins: [
plugin(function({ matchUtilities, theme }) {
matchUtilities(
{
'translate-z': (value) => ({
'--tw-translate-z': value,
transform: ` translate3d(var(--tw-translate-x), var(--tw-translate-y), var(--tw-translate-z)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))`,
}), // this is actual CSS
},
{ values: theme('translate'), supportsNegativeValues: true }
)
})
],
}
DEMO
I have this style:
box-shadow: 0 0 0 100px white inset;
But I don't want white hard-coded in - I want to make a Tailwind plugin in my tailwind.config.js called bg-shadow that accepts the colour as an optional third argument, like so:
<input class="bg-shadow" /> <!-- defaults to white -->
<input class="bg-shadow-primary-500" /> <!-- specify the colour -->
Having read through the Tailwind docs on plugins, I am none the wiser as to how to achieve this!
You could do this with a pretty simple plugin using matchUtilities and flattenColorPalette.
In tailwind.config.js:
const plugin = require("tailwindcss/plugin");
const { default: flattenColorPalette } = require("tailwindcss/lib/util/flattenColorPalette");
module.exports = {
...
plugins: [
plugin(({ matchUtilities, theme }) => {
matchUtilities(
{
"bg-shadow": (value) => ({
boxShadow: `0 0 0 100px ${value} inset`
})
},
{
values: flattenColorPalette(theme("colors")),
type: "color"
}
);
})
]
};
After studying this plugin (credit where credit's due) I have this now working as follows:
// tailwind.bg-shadow.plugin.js
const plugin = require("tailwindcss/plugin");
const formatShadow = (color) => `0 0 0 100px ${color} inset`;
module.exports = plugin.withOptions(
({className = "bg-shadow"} = {}) => {
return ({e, addUtilities, theme, variants}) => {
const colors = theme("colors");
const caretColors = Object.keys(colors).reduce((acc, key) => {
if (typeof colors[key] === "string") {
return {
...acc,
[`.${className}-${e(key)}`]: {
"box-shadow": formatShadow(colors[key])
},
};
}
const colorShades = Object.keys(colors[key]);
return {
...acc,
...colorShades.reduce(
(a, shade) => ({
...a,
[`.${className}-${e(key)}-${shade}`]: {
"box-shadow": formatShadow(colors[key][shade])
},
}),
{}
),
};
}, {});
addUtilities(caretColors, variants("bg-shadowColor"));
};
},
() => ({
variants: {caretColor: ["dark", "active"]},
})
);
And usage in tailwind.config.js:
module.exports = {
...
plugins: [
require('./tailwind.bg-shadow.plugin')
],
}
I created a custom color on tailwind in next js. On localhost the defined color appears fine, but when I deploy to vercel the color doesn't appear.
here's the picture localhost
production in vercel
tailwind.config.js
const colors = require('tailwindcss/colors');
module.exports = {
purge: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}'
],
darkMode: false, // or 'media' or 'class'
theme: {
colors: {
transparent: 'transparent',
current: 'currentColor',
black: {
DEFAULT: '#23232D'
},
white: colors.white,
gray: {
DEFAULT: '#A1A1A1',
},
...
}
},
variants: {
extend: {},
},
plugins: [],
}
ButtonColor/index.js
import PropTypes from 'prop-types';
import { motion } from 'framer-motion';
function ButtonColor({ color, isOpen, onClick }) {
const variants = {
open: { width: '100%' },
closed: { width: '50%' },
}
return (
<motion.div
className={`bg-${color} h-6 cursor-pointer`}
onClick={onClick}
animate={isOpen ? "open" : "closed"}
variants={variants}
>
</motion.div>
)
}
ButtonColor.propTypes = {
color: PropTypes.string.isRequired,
isOpen: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
}
export default ButtonColor;
Any solutions for this case? thanks.
You can't use string concatenation to create CSS class names because PurgeCSS won't know to preserve your custom classes during the build process.
className={`${color === 'red' ? 'bg-red' : 'bg-blue'} h-6 cursor-pointer`}
Alternatively, you can create a custom global CSS/SCSS file. In that file, define your styles using tailwindcss directives.
global.{css|scss}
.button {
#apply h-6;
#apply cursor-pointer;
&.red{
#apply bg-red-700 dark:bg-red-900;
#apply text-white;
#apply hover:bg-red-800 dark:hover:bg-red-800;
}
&.gray {
#apply bg-gray-300 dark:bg-gray-600;
#apply text-gray-900 dark:text-gray-200;
#apply hover:bg-gray-400 dark:hover:bg-gray-500;
}
}
<motion.button className="button"> ...
Side note - motion.div should be motion.button
I have started using Gutenberg custom blocks, and I have built custom blocks successfully!
But I have an issue, I need to see the preview of the block when I am not editing it on the Admin side, I don't think Gutenberg provides it, because I searched the documentation, searched the internet but nothing found regarding preview of the block when not in edit state.
I have used Block Lab Plugin and it do have a preview, if Gutenberg don't provide it, how can I have a preview? Any Trick or Hack?
This is my block.js:
(function(blocks, components, element) {
var el = element.createElement;
var registerBlockType = blocks.registerBlockType;
var TextControl = components.TextControl;
registerBlockType("blocks/test-block", {
title: "Test Block",
description: "A custom block.",
icon: "businessman",
category: "common",
attributes: {
headline: {
type: "string"
}
},
edit: function(props) {
var headline = props.attributes.headline;
var onHeadlineChange = function(val) {
props.setAttributes({
headline: val
});
};
return el(TextControl, {
value: headline,
label: "Headline",
onChange: onHeadlineChange
});
},
save: function(props) {
return el(
"h3",
{
className: "headline"
},
props.attributes.headline
);
}
});
})(window.wp.blocks, window.wp.components, window.wp.element);
To display your custom block with a preview, just add example: () => {}, to section, in the same way as save and edit.
https://prnt.sc/rov8qo
//Custom Gutenberg block
(function(components, element) {
//Default
const { registerBlockType } = wp.blocks;
//Block registration
registerBlockType('', {
title: '',
description: '',
icon: '',
category: '',
keywords: [''],
attributes: {},
//Example function
example: () => {}, //Add this to get block preview works
//Edit function
edit: props => {},
//Save function
save: props => {}
});
})(window.wp.components, window.wp.element);
UPDATE 1
In order to make a preview of the block (how it will appear in the front), you need to add styles to this block, the same as on the front, and track props.isSelected this block in editor.
Depending on whether the block is selected or not, show different content.
//Custom Gutenberg block
(function(blocks, element, blockEditor) {
//Default variable
const { registerBlockType } = blocks;
const { createElement: el } = element;
const { RichText } = blockEditor;
//Block registration
registerBlockType("blocks/test-block", {
title: 'Test Block',
description: 'A custom block.',
icon: 'businessman',
category: 'common',
attributes: {
title: {
type: 'string',
source: 'html',
selector: '.headline'
}
},
//Example function
example: () => {},
//Edit function
edit: props => {
const attributes = props.attributes;
return (
el( 'div', { className: props.className },
//View input field
props.isSelected && el(RichText, {
tagName: 'h3',
placeholder: 'Headline...',
keepPlaceholderOnFocus: true,
value: attributes.title,
allowedFormats: [],
onChange: title => props.setAttributes({ title })
}),
//View frontend preview
!props.isSelected && el( 'div', { className: 'block__headline' },
el( 'div', { className: 'block__headline-title' }, attributes.title ? attributes.title : 'Entry headline...')
)
)
);
},
//Save function
save: props => {
return el( RichText.Content, {
tagName: 'h3',
className: 'headline',
value: props.attributes.title
});
}
});
})(window.wp.blocks, window.wp.element, window.wp.blockEditor);
Css the same as on your frontend.
.block__headline {
padding: 20px 15px 30px;
background: #fafafa;
text-align: center;
}
.block__headline-title {
font-family: 'Montserrat';
font-size: 30px;
font-weight: bold;
position: relative;
}
.block__headline-title:after {
content: '';
display: block;
width: 40px;
height: 2px;
background: #333;
margin: 0 auto;
}