ui icon indicating copy to clipboard operation
ui copied to clipboard

RTL Support

Open nahasco opened this issue 2 years ago โ€ข 44 comments

RTL support for all components in the registry.

Mainly replaced physical properties with logical properties and added rtl:space-x-reverse where space-x-* is used.

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

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

nahasco avatar Sep 30 '23 22:09 nahasco

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

A member of the Team first needs to authorize it.

vercel[bot] avatar Sep 30 '23 22:09 vercel[bot]

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.

shadcn avatar Oct 03 '23 14:10 shadcn

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.

nahasco avatar Oct 03 '23 15:10 nahasco

@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.

shadcn avatar Oct 19 '23 17:10 shadcn

@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!

nahasco avatar Oct 22 '23 10:10 nahasco

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

tomer-yechiel avatar Oct 26 '23 20:10 tomer-yechiel

It would be great to have it rn ๐Ÿ˜…

mustafamoe avatar Dec 07 '23 17:12 mustafamoe

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.

MHBahrampour avatar Sep 06 '24 00:09 MHBahrampour

any update on this?

sahandsn avatar Sep 26 '24 17:09 sahandsn

Waiting for updates!

salahkai avatar Oct 02 '24 16:10 salahkai

I will continue working on this in the next few days. I am hoping I add RTL support to as much components as I can before I open the PR again. Then we will have to wait for it to get merged. In the meantime, you may copy the RTL supported components from this PR.

nahasco avatar Oct 02 '24 16:10 nahasco

Hey @shadcn , can you share some input on this? Any plans of merging?

nahasco avatar Oct 29 '24 17:10 nahasco

@nahasco Yes. Let me add this to the roadmap. I have had this on my list for a while. This should be easier now I think with the new cli and registry.

shadcn avatar Oct 29 '24 18:10 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?

shadcn avatar Oct 29 '24 18:10 shadcn

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?

nahasco avatar Oct 29 '24 19:10 nahasco

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

iskaa02 avatar Nov 09 '24 01:11 iskaa02

Subscribing to this!

nitzanpap avatar Nov 11 '24 23:11 nitzanpap

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

jjeem avatar Nov 20 '24 15:11 jjeem

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

Could you help with the remaining components?

nahasco avatar Nov 25 '24 22:11 nahasco

This is mazing following ๐Ÿ™

omerman avatar Nov 27 '24 06:11 omerman

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.

nahasco avatar Nov 27 '24 09:11 nahasco

@nahasco You are a legend! I cant wait for this to be merged๐Ÿ‘๐Ÿ‘

omerman avatar Nov 27 '24 09:11 omerman

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

vercel[bot] avatar Dec 06 '24 19:12 vercel[bot]

Thanks to @jjeem , all components are completed and the PR is ready to merge.

@shadcn

nahasco avatar Dec 13 '24 12:12 nahasco

@nahasco wow, great work. I'll review.

shadcn avatar Dec 14 '24 10:12 shadcn

This is amazing cant wait for it be merged !

MohamedAlQadeery avatar Dec 14 '24 12:12 MohamedAlQadeery

Can we do something to speed up the merge ?

ioeldev avatar Dec 20 '24 22:12 ioeldev

@shadcn We are waiting for the merged

alialnaghmoush avatar Dec 21 '24 08:12 alialnaghmoush

Waiting for this to be merged

200-0K avatar Dec 22 '24 14:12 200-0K

Same here, would love to see it too

omerman avatar Dec 22 '24 18:12 omerman