@@ -171,6 +171,83 @@ const noUnstableMethods = {
171171 } ,
172172} ;
173173
174+ const noPhysicalCssProperties = {
175+ meta : {
176+ type : 'problem' ,
177+ docs : {
178+ description : 'Enforce use of CSS logical properties instead of physical properties for RTL support' ,
179+ recommended : false ,
180+ } ,
181+ messages : {
182+ useLogicalProperty :
183+ 'Use logical CSS property "{{logical}}" instead of physical property "{{physical}}" for RTL support.' ,
184+ useLogicalTextAlign :
185+ 'Use logical textAlign value "{{logical}}" instead of physical value "{{physical}}" for RTL support.' ,
186+ } ,
187+ schema : [ ] ,
188+ } ,
189+ create ( context ) {
190+ // Mapping of physical properties to logical equivalents
191+ const propertyMap = {
192+ left : 'insetInlineStart' ,
193+ right : 'insetInlineEnd' ,
194+ marginLeft : 'marginInlineStart' ,
195+ marginRight : 'marginInlineEnd' ,
196+ paddingLeft : 'paddingInlineStart' ,
197+ paddingRight : 'paddingInlineEnd' ,
198+ borderLeft : 'borderInlineStart' ,
199+ borderRight : 'borderInlineEnd' ,
200+ borderLeftWidth : 'borderInlineStartWidth' ,
201+ borderRightWidth : 'borderInlineEndWidth' ,
202+ borderLeftStyle : 'borderInlineStartStyle' ,
203+ borderRightStyle : 'borderInlineEndStyle' ,
204+ borderLeftColor : 'borderInlineStartColor' ,
205+ borderRightColor : 'borderInlineEndColor' ,
206+ borderTopLeftRadius : 'borderStartStartRadius' ,
207+ borderTopRightRadius : 'borderStartEndRadius' ,
208+ borderBottomLeftRadius : 'borderEndStartRadius' ,
209+ borderBottomRightRadius : 'borderEndEndRadius' ,
210+ } ;
211+
212+ const checkProperty = ( key , value ) => {
213+ const keyName = key . type === 'Identifier' ? key . name : key . value ;
214+
215+ // Check for physical property names
216+ if ( propertyMap [ keyName ] ) {
217+ context . report ( {
218+ node : key ,
219+ messageId : 'useLogicalProperty' ,
220+ data : {
221+ physical : keyName ,
222+ logical : propertyMap [ keyName ] ,
223+ } ,
224+ } ) ;
225+ }
226+
227+ // Check for textAlign with physical values
228+ if ( keyName === 'textAlign' && value ) {
229+ if ( value . type === 'Literal' && ( value . value === 'left' || value . value === 'right' ) ) {
230+ const logicalValue = value . value === 'left' ? 'start' : 'end' ;
231+ context . report ( {
232+ node : value ,
233+ messageId : 'useLogicalTextAlign' ,
234+ data : {
235+ physical : value . value ,
236+ logical : logicalValue ,
237+ } ,
238+ } ) ;
239+ }
240+ }
241+ } ;
242+
243+ return {
244+ Property ( node ) {
245+ checkProperty ( node . key , node . value ) ;
246+ } ,
247+ } ;
248+ } ,
249+ } ;
250+
174251export default tseslint . config ( [
175252 {
176253 name : 'repo/ignores' ,
@@ -248,6 +325,7 @@ export default tseslint.config([
248325 rules : {
249326 'no-global-object' : noGlobalObject ,
250327 'no-unstable-methods' : noUnstableMethods ,
328+ 'no-physical-css-properties' : noPhysicalCssProperties ,
251329 } ,
252330 } ,
253331 'simple-import-sort' : pluginSimpleImportSort ,
@@ -458,6 +536,13 @@ export default tseslint.config([
458536 'custom-rules/no-unstable-methods' : 'error' ,
459537 } ,
460538 } ,
539+ {
540+ name : 'packages/ui' ,
541+ files : [ 'packages/ui/src/**/*' ] ,
542+ rules : {
543+ 'custom-rules/no-physical-css-properties' : 'error' ,
544+ } ,
545+ } ,
461546 {
462547 name : 'packages - vitest' ,
463548 files : [ 'packages/*/src/**/*.test.{ts,tsx}' ] ,
0 commit comments