Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTL Support #1638

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open

RTL Support #1638

wants to merge 27 commits into from

Conversation

nahasco
Copy link

@nahasco nahasco commented Sep 30, 2023

RTL support for almost all components in the registry.

Mainly replaced physical properties with logical properties and added rtl:space-x-reverse where space-x-* is used. In addition to using radix direction provider to return chevrons dynamically.

This fixes #530, fixes #4877 , fixes #5776, fixes #5658 , fixes #2759 , fixes #2736 , fixes #1919

Up to date list of adjusted components:

"🔸" means the component didn't need any changes to be compatible with RTL layouts

  • Accordion 🔸
  • Alert Dialog
  • Alert
  • Aspect Ratio 🔸
  • Avatar 🔸
  • Badge 🔸
  • Breadcrumb
  • Button 🔸
  • Calendar
  • Card 🔸
  • Carousel
  • Chart🔸
  • Checkbox 🔸
  • Collapsible 🔸
  • Command
  • Context Menu
  • Dialog
  • Drawer
  • Dropdown Menu
  • Form
  • Hover Card
  • Input OTP
  • Input 🔸
  • Label 🔸
  • Menubar
  • Navigation Menu
  • Pagination
  • Popover 🔸
  • Progress🔸
  • Radio Group 🔸
  • Resizable 🔸
  • Scroll Area 🔸
  • Select
  • Separator 🔸
  • Sheet
  • Skeleton
  • Slider 🔸
  • Sonner 🔸
  • Switch
  • Table
  • Tabs 🔸
  • Textarea 🔸
  • Toast
  • Toaster
  • Toggle Group 🔸
  • Toggle 🔸
  • Tooltip 🔸
  • Sidebar

@vercel
Copy link

vercel bot commented Sep 30, 2023

Someone is attempting to deploy a commit to the shadcn-pro Team on Vercel.

A member of the Team first needs to authorize it.

@shadcn
Copy link
Collaborator

shadcn commented Oct 3, 2023

This is interesting. I wonder if we could have this as an option via the cli. Turn on rtl in components.json and we automatically apply those updates for you.

@nahasco
Copy link
Author

nahasco commented Oct 3, 2023

This is interesting. I wonder if we could have this as an option via the cli. Turn on rtl in components.json and we automatically apply those updates for you.

That's a great idea!

The changes I have added result in support of both RTL and LTR directions, by simply adding dir=rtl to the html element <html dir="rtl"> and finally wrapping the app with Radix's direction provider <DirectionProvider dir=rtl> to switch to RTL. Furthermore, when changing the properties from directional to logical I realised some icons like ChevronLeft or ChevronRight also need to be adjusted for RTL. One more minor change would be adjusting the animation directions.

Acheiving all of this via the cli would result in full RTL support, but I am not sure how can it be done.

@shadcn
Copy link
Collaborator

shadcn commented Oct 19, 2023

@nahasco I can take a look at this. Having this in the cli would be awesome. Adding it to the next milestone. I'll push to this PR and make sure you're credited for the work you've done here. Thank you.

@nahasco
Copy link
Author

nahasco commented Oct 22, 2023

@nahasco I can take a look at this. Having this in the cli would be awesome. Adding it to the next milestone. I'll push to this PR and make sure you're credited for the work you've done here. Thank you.

Regarding the choice between physical and logical properties, I'd like to clarify whether you intend to use logical properties as the default for all languages. Logical properties offer the flexibility needed for seamless direction switching, and I believe they could simplify the codebase and improve adaptability for various languages.

For other specific elements like icons and HTML or radix direction values, there could be an option to manage these through the CLI to give a starting ground to the user. This approach allows us to make logical properties the primary choice for simplicity and flexibility while maintaining control over other elements that may require manual adjustments.

I'm curious to know your thoughts on this approach and whether logical properties are indeed the direction you're leaning towards. Your input on this would be highly valuable. Once again, thank you for your contribution to this!

@tomer-yechiel
Copy link

please keep in mind the use case of supporting both LTR and RTL in the same codebase.

@mustafamoe
Copy link

It would be great to have it rn 😅

@shadcn shadcn mentioned this pull request Dec 21, 2023
@nahasco nahasco changed the title RTL support for almost all registry components and examples and a bug fix RTL support for almost all registry components and examples Mar 4, 2024
@@ -65,7 +65,7 @@ const AlertDialogFooter = ({
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 rtl:space-x-reverse",
Copy link

@dodeca-6-tope dodeca-6-tope Jul 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-2? I think it'd be cleaner.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didnt want to change the original styling alot. Just adding rtl:space-x-reverse at the end is the most minimal, i think.

@MHBahrampour
Copy link

It's really shocking that a modern UI library doesn't fully support RTL or Internationalization (like calendar). Love shadcn but I with they would consider these stuff more seriously.

@nahasco nahasco reopened this Sep 11, 2024
@nahasco nahasco changed the title RTL support for almost all registry components and examples RTL Support Sep 11, 2024
@shadcn shadcn added the area: roadmap This looks great. We'll add it to the roadmap, review and merge. label Oct 29, 2024
@shadcn shadcn self-assigned this Oct 29, 2024
@shadcn
Copy link
Collaborator

shadcn commented Oct 29, 2024

@nahasco question. is there a 1:1 mapping for those utility classes? I wonder if we could write a transformer to do these on install?

@nahasco
Copy link
Author

nahasco commented Oct 29, 2024

Thank you @shadcn

@nahasco question. is there a 1:1 mapping for those utility classes? I wonder if we could write a transformer to do these on install?

I am not sure what that means. But I didn't just adjust the classes, in some components I used radix ui's direction provider to pass the dir value to the component.

For example the carousel component requires a dir value to make it rtl or ltr.

I believe RTL support should be the default for the entire library and not just an option in the cli. The changes I made make the components support rtl and ltr dynamically based on the html dir value and radix's direction provider.

In a case where a user chooses not to support RTL in the cli, but later on decides to support RTL, they will have to refactor everything. So why not make RTL support the default?

@iskaa02
Copy link

iskaa02 commented Nov 9, 2024

My app support both RTL and LTR, For my usecase, I updated the code for RTL support by globally replacing left- with start- and right- with end-. This included properties like margins ml mr to ms me, padding pl pr to ps pe, and text alignment text-left to text-start, ...etc.
also some bugs occurred from the translate-x class so for every translate-x i would append rtl:-translate-x.

For the sidebar, I just changed the side based on the lang

for dialogs, popovers, anything that would use a portal, i wrapped the children with a div with dir tag because it's not inherited from the root

// src/components/ui/dialog.tsx
const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <div dir={languageTag() == "ar" ? "rtl" : "ltr"}>
      <DialogOverlay />
      <DialogPrimitive.Content
        ref={ref}
        className={cn(
          "fixed start-[50%] top-[50%] z-50 grid w-full max-w-lg rtl:translate-x-[50%] translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
          className,
        )}
        {...props}
      >
        {children}
        <DialogPrimitive.Close className="absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
          <Cross2Icon className="h-4 w-4" />
          <span className="sr-only">Close</span>
        </DialogPrimitive.Close>
      </DialogPrimitive.Content>
    </div>
  </DialogPortal>
));

Still facing small bugs here and there but nothing major

@nitzanpap
Copy link

Subscribing to this!

@jjeem
Copy link

jjeem commented Nov 20, 2024

Great work!
anything I can help with to push this ASAP?

@nahasco
Copy link
Author

nahasco commented Nov 25, 2024

Great work!
anything I can help with to push this ASAP?

Could you help with the remaining components?

@omerman
Copy link

omerman commented Nov 27, 2024

This is mazing following 🙏

@nahasco
Copy link
Author

nahasco commented Nov 27, 2024

My app support both RTL and LTR, For my usecase, I updated the code for RTL support by globally replacing left- with start- and right- with end-. This included properties like margins ml mr to ms me, padding pl pr to ps pe, and text alignment text-left to text-start, ...etc.
also some bugs occurred from the translate-x class so for every translate-x i would append rtl:-translate-x.

For the sidebar, I just changed the side based on the lang

for dialogs, popovers, anything that would use a portal, i wrapped the children with a div with dir tag because it's not inherited from the root

// src/components/ui/dialog.tsx
const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <div dir={languageTag() == "ar" ? "rtl" : "ltr"}>
      <DialogOverlay />
      <DialogPrimitive.Content
        ref={ref}
        className={cn(
          "fixed start-[50%] top-[50%] z-50 grid w-full max-w-lg rtl:translate-x-[50%] translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
          className,
        )}
        {...props}
      >
        {children}
        <DialogPrimitive.Close className="absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
          <Cross2Icon className="h-4 w-4" />
          <span className="sr-only">Close</span>
        </DialogPrimitive.Close>
      </DialogPrimitive.Content>
    </div>
  </DialogPortal>
));

Still facing small bugs here and there but nothing major

This is similar to what I did in this PR. Additionally, I also added a dir state to some components and added rtl:rotate-180 to left and right chevron icons to fully support RTL.

@omerman
Copy link

omerman commented Nov 27, 2024

@nahasco You are a legend!
I cant wait for this to be merged👏👏

Copy link

@Aymen-Khanfir Aymen-Khanfir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, i was thinking of use rlt[] and ltr[] but you solution required less code

Copy link

vercel bot commented Dec 6, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
ui ❌ Failed (Inspect) Dec 11, 2024 7:22pm

RTL support for more components
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: cli area: component area: roadmap This looks great. We'll add it to the roadmap, review and merge. enhancement New feature or request
Projects
None yet