Tailwind UI的建立反应和VUE支持

2021-04-13 04:51:51

嘿!我们'重新获得释放React + Vue支持TaiLwind UI,所以我认为分享甚至可能陷入困境的努力将是有趣的。

从2019年中期的某个地方开始工作的那一天,我知道,如果他们可以使用他们最喜欢的JS框架建造的完全互动的例子,那么人们就会更加有价值。试图让第一个发布的事情变得太雄心勃勃,所以我们必须弄清楚如何一次到达一步一步。

我们决定首先关注香草HTML,因为它完全普遍,即使JSX像JSX这样的东西对某些人更有用,那么有很多现有的工具将HTML转换为人们可以依赖的JSX 。

我们还使硬折衷off-of of提供任何JS进行交互,如在第一个版本中切换响应菜单或打开和关闭模态对话框。我觉得我们提供的任何东西都会做得更弊大于,因为那里没有一个JS框架,可以弥补大部分尾风用户群。如果我们愿意反应开发人员,我们&#39如果我们迎合Vue开发人员,我们&#39如果我们试图在自定义vanilla js中写它,我们' d' d' d的字面上更难(认真对待建立一个强大的输入/离开转换系统,从js划痕拍摄多少钱? )

因此,我刚刚使用HTML中的注释记录了不同的状态,并将其留给最终用户以将其连接到他们最喜欢的JS框架。我知道很多人都喜欢关于宽容的人,我认为我们也是一个很好的方法。

但是,一旦我们觉得莱德风UI很巧妙地用数百个很棒的例子,我们决定是时候解决了JS问题,看看我们能做什么。

作为抽象概念添加" JavaScript支持"到TaiLwind UI声音直截了当,但是当您挖掘详细信息时它不是。有很多决定甚至建立什么,以及在试图为尽可能多的人做出有用的东西时需要考虑许多权衡。

在我实际上有一个计划之前,我在尾风UI工作时扔了整个概念的整个概念整整一年。最终,这些是在设计解决方案时决定的核心值:

TailWind UI的承诺是它' s只是一个代码片段 - 它'通过直接编辑代码,易于自定义和调整。我们提供的任何JS示例需要尊重这一基础想法。

JS需要可更新。与我们希望人们完全拥有和编辑他们的心灵的标记不同,JS需要以某种方式来自Node_Modules,因为建立这些事情是艰难的,我们想要的错误,我们想要能够为人们修复他们,而不要求他们复制新的代码片段。在那之上,我们希望人们必须仔细运送200行的JS,他们在他们的代码库上写下,并不断担心错误地错误地打破了一些小的实施细节。

它只是比香草HTML更好。在一天结束时,最重要的是,我们为使用JS框架的人们提供了更好的经验,我们决定首先添加支持。任何时候我发现自己被两个竞争的权衡沮丧,这使得很难做出完美的东西,问自己和#34;这仍然是严格的,框架X用户比香草HTML更糟糕的方式更好吗?提供了很多清晰度。

对我来说非常重要的另一件事是,底层JS的东西都没有专有的或尾风UI特定的。对我来说,Tailwind UI不是像蚂蚁设计或材料的UI套件 - 那些是伟大的项目,但它不是我想建造的。

对我来说,TaiLwind UI是一系列蓝图,向您展示如何使用已经可用的工具构建令人敬畏的东西。如果你想用完全按照他们从架子上的东西来使用,你完全可以和你' ll获得了最佳结果。但是,您还应该能够使用Tailewind UI作为一个有用的起点,将其调整到九个,并最终与您感到独特的东西,即使我们在一开始就提升了升级。

因此,在我们可以为Tailwind UI添加JavaScript支持之前,我们需要构建一些工具。

几年前我记得看到肯特C. Dodds' Downswift图书馆和思维"男人,这是一个很酷的概念 - 所有复杂的行为都藏起在图书馆里,但所有的实际标记和造型都留给了用户"

这种方法是尾风哲学的完美契合,因为Tailewind的整个目标是帮助您更快地建立完全定制的设计。 Tailwind +一个JS组件库,摘要所有键盘导航和可访问性逻辑,而不包括任何设计意见是如此强大的组合 - 它会让团队建设完全定制的UIS几乎与以满足于努力的团队一样快速地移动 - 定制,自以为是框架。

我们看看是否有任何其他工具在那里解决了同样的问题,而在空间中有一些很棒的项目(特别是当时到达UI和reakit,而且自从我们自己的图书馆开始以来的反应 - aria以来所有这些人的工作),最终我们决定为我们公司这么重要的事情将是最好的建立和控制自己。

我们希望API与像尾风一样的基于类的造型解决方案一起使用。许多其他工具出现在那里期待您编写自定义CSS来定位每个组件的不同位,这与您用来用尾风样式的工作流的工作流相同。我们想设计一下非常级别的友好。

我们希望使用一致的API支持多个框架。有反应图书馆,Vue库,角图书馆等,但每个人都不同,由不同的口味设计不同的人。我们想要从框架到框架的框架尽可能一致的东西,以便尾行UI中的框架特定示例' t彼此彻底不同。

我真的很兴奋到最后,我们将最终结束,但圣洁的废话这将是很多工作。

我们决定打电话给这个项目"无头UI"并于去年8月罗宾马尔公仔加入球队全职工作,几乎完全。

他工作的第一件事是对反应的过渡组件,可以允许您完全使用类添加Enter / Leave动画,并由<转换和gt非常启发。 Vue中的组件:

<转换显示= {ISOpen} Enter ="过渡透明度持续时间-75" experfrom ="透明度-0" onerto ="透明度-100"离开="过渡透明度持续时间-150"离开="透明度-100" Leaveto ="透明度-0" >我会淡入和换取和效果。/过渡>

当我说我们真正想要设计"班级友好&#34的组件时,这是我之前的意思的一个很好的例子。此组件使您的Enter / Leaving Transitions与常规旧的Tailewind实用程序类风格很容易,因此它感觉就像在您的应用中的其他任何内容一样。它'也没有以任何方式耦合到尾风,并且您可以使用您想要的任何类!

我们在10月份发布了第一个公开发布,其中包括前三个组件的反应和Vue库:

我们降落在一组使用的API中使用"复合组件"通过上下文互相通信(或提供/注入Vue)时摘要所有复杂性。

从&#39导入{菜单}; @ headlessui /反应'函数mydropdown(){return(<菜单为=" div" classname ="相对">>>> button classname =" px-4 py- 2圆形的bg-blue-600文本白色...">选项< / menu.button>< menum.items classname =" absolute mt-1右 - 0" >< menum.item> {({artive})=>(< classname = {`$ {active&' bg-blue-500 text-white'}。 ..`} href =" /账户设置">帐户设置< / a>)}< / menum.item>>>> {({artive} )=>(< classname = {`$ {Active&' bg-blue-500 text-white'} ...`} href =" / documentation&# 34;>文档< / a>)}< / menu.item>< menum.item disabled><>" opacity-75 ...""邀请朋友(即将推出!)< / span>< / menu.item>&l lt; / menu .ITEMS> < / menu> )}

你' ll注意到做风格的东西和#34;活跃"下拉项,我们使用Render Prop(或Vue中的范围):

< menu.item> {({Active})=> (< classname = {`$ {活动&' bg-blue-500 text-white'} ...`} href =" /文件">文档< / a>)}< / menu.item>

Render Props Aren'它通常是常见的,因为他们曾经是因为钩子在许多情况下取代了他们的需要。但是对于这种问题,您需要访问内部状态,'由组件管理的内部状态'重新消耗,它们仍然是正确的(仅限)解决方案,非常优雅。

在10月份发布第一版本的无头UI后,我们向下扣上了几个月的时间来发布Tailwind CSS v2.0,然后花在最后一个月的时间集中在休息前的错误修复和许多项目房子保持扣押为假期。

当我们回来时,我们难以努力完成实际增加反应+ Vue支持,以便我们需要的第一件事是审核我们在Tailwind UI中所需的所有互动行为,并弄清楚我们需要设计的无头UI抽象。

这实际上是一个非常有趣和有挑战性的工作,因为它真的并不总是显而易见的是,某种设计特定的交互应该如何映射到具有已知可访问性期望的建立的UI模式。

但有些人很棘手。例如,移动菜单怎么样,你用汉堡包按钮打开的东西?

如果它刚刚打开它,该怎么用下一页又一次地推开?

我们经常这样的问题工作,并在良好的解决方案上降落了很多研究和实验。我们'重新幸运地在团队中拥有大卫Luhr,他们一直专注于漫长的可访问性,并在他的帮助下,我们能够觉得我们降落的解决方案真的很好。

在这里,我们决定我们需要的是什么,以支持Tailwind UI已经存在的模式:

菜单按钮。用于下拉菜单,只包含链接或按钮,就像表行末尾的小动作菜单一样。

列表框。有关自定义选择要在选项元素中包含额外内容的自定义选择。例如,您将在每个国家/地区放置旗帜的国家选择器。

披露。将含量显示/隐藏到位。认为像崩溃的常见问题解答问题一样。对于更大的UI块也很有用,例如,就像一个打开的移动菜单一样打开并按下其余页面。

对话。好吧,模态对话!而且对于从页面侧面滑出的移动导航,以及其他"即使他们没有看起来像一个传统的面板中心 - 屏幕模态。

弹出窗口。对于单击按钮时弹出页面顶部的面板。这对菜单很有用,您需要大量的自定义内容,这些内容将违反常规角色的严格性="菜单"菜单按钮。我们将这些用于一些移动菜单,在导航栏中的弹性菜单以及其他有趣的地方。它'那种类似菜单/披露混合。

无线电群。对于自定义无线电选择UI,如您想要一组可点击的卡而不是无聊的小无线电圈。

我们遇到了大量的挑战,尤其是焦点管理等复杂的东西,特别是嵌套焦点管理。

成像你有一个莫代尔,打开,在那里的模态里面有一个下拉列表。你打开模态,然后打开下拉目,然后命中逃生。怎么了?下拉下降应该右转,但模态应该保持开放。

我保证在这种情况下,互联网上的99%的模态在这种情况下也会接近,即使他们应该争夺' t应该。但不是我们的 - 我们的作品!

我们(主要是罗宾)花了几个月的几个细节,如此,使一切都能尽可能地成为子弹证明,而我肯定必须有臭虫仍然在某个地方藏起,我们最终却被摇滚乐与几乎所有UI相比,您在网络上遇到的几乎所有UI。

我们仍然有很多新的模式我们想要添加到无头UI,如标签,手风琴,也许甚至是古代古怪的诡辩,我们'我们期待着探索未来的其他框架(Alpine.js是我们的名单上的下一个框架)但是,我们'重新激动地称之为我们'重新发布本周的无头UI v1.0并提交稳定的API。

使用无头UI的ui upost out out,下一个大问题是究竟究竟是一个现有的尾风UI示例的反应或Vue版本应该是什么样的。

TailWind UI中的示例是纯HTML片段 - 您找到了您喜欢的东西,将HTML复制到您的项目中,然后将其调整为您喜欢的方式,将其切换为单个组件,无论您想要什么。我们不会对你如何使用它,&#39做出任何假设

使用纯HTML时,这是一个轻松的决定 - 您真的有什么选择?但是在提供框架特定的例子时,它会恰好了解所提供的内容。

最大的问题是我们应该努力消除任何重复,以及做什么方法是什么?

反应和VUE都是组件框架,以及您在项目中重用代码的方式是通过将UI的位提取到您可以在遍历一遍又一遍地使用的组件。

挑战是创建像这样的组件,总是是特定的项目。占用此列表组件:

例如,它假设项目是团队成员。如果您'重新构建发票应用程序,您希望使用此模式以获取客户的列表吗?地狱,你可能会用这个运动赌注应用程序,这些应该是棒球队,甚至不是人!

它还使构件对象的形状做出假设。它必须编码它'删除名称和电子邮件属性,即使您的数据可能不同。

另一个问题是,在像Vue这样的框架中,每个文件只能有一个组件。这意味着复制由4-5个子组件组成的示例意味着您必须复制4-5个不同的片段,为每个播放片段创建文件,并将它们全部与正确的名称/路径一起链接。

对我来说,关于人们对人们觉得这一切的事情感觉太过分了,至少对于我们今天试图解决的问题。当一切都像预定义的道具API一样超级打破,并且故意选择组件名称时,它感觉像你aren' t应该知道它了。我对Tailwind UI的热爱是点击"代码"选项卡感觉就像打开一些复杂的electonics,并在你面前看到所有的电路。它'是一个学习机会,你可以阅读标记和类名并了解如何将其工作在一起。

我长期以来摔倒了,但最终决定了现在我们试图解决两个主要问题:

使用他们实际需要的语法给予人们代码,例如给出反应用户JSX而不是HTML,所以他们不得不手动将类似于Labelfor的东西转换为Labelpor。

让交互式元素开箱即用,所以下拉,移动菜单,切换,以及其他一切都准备好了,而不是必须自己写下所有的样板JS。

我决定的是,正确的解决方案是专注于解决这些问题,并注意不要做任何将尾风U​​I转换为不同产品的任何东西。

因此,与Vanilla HTML版本相比,当您查看React或Vue示例时,这是'

每个框架示例使用正确的语法 - React示例使用JSX,并且在单文件组件语法中提供了VUE示例。

转换现在是真实的 - 而不是评论,而不是在转换的每个阶段添加什么类,转换就在那里,使用无头UI转换组件或Vue'原生转换组件。

互动元素由无头UI处理 - 您' LL在任何示例中看到一些需要js的少数导入,其中我们拉到所需的无需UI组件,然后直接在标记中使用。

任何重复的标记块已被转换为基本循环 - 任何数据驱动的循环填充(如人员名单或导航项目)都会在示例中提取到简单变量中,以减少复制,但仍将所有内容保持在一个地方。在您自己的项目中,您' d与来自API或数据库的数据或任何内容换句话说,但我们保持简单和DON' T对您做出任何假设。

图标从海英语库中拉入。只要使用图标,我们从我们的React / Vue图标库中将它们拉到On In而不是保持标记更简单时,而不是将其拉入SVG。

导入{菜单,转换}从' @ headlessui /反应'从&#39导入{dotsverticalicon}; @ hericons /反应/ solid'从&#39导入{碎片};反应' const people = [{name:'凯文霍金斯' ,电子邮件:' [email protected]' ,图像:' https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib = rb-1.2.1& ixid = eyjhchbfawqiojeymdd9& auto = format& faceearea& facepad = 2& w = 256& w = 256& w = 256& w = 256& w = 256& ; h = 256& q = 80' ,},{名:'克里斯汀·拉莫斯' ,电子邮件:' [email protected]' ,图像:' https://images.unsplash.com/photo-1550525811-205869dd03032?ixlib = rb-1.2.1& ixid = eyjhchbfawqiojeymdd9& auto = format& facearea& facepad = 2& w = 256& w = 256& w = 256& w = 256& ; h = 256& q = 80' ,},{名:' ted fox' ,电子邮件:' [email protected]'图像:' HTTPS:?//images.unsplash.com/photo-1500648767791-00dcc994a43e ixlib = RB-1.2.1&安培; ixid = eyJhcHBfaWQiOjEyMDd9&安培;汽车=格式&安培;配合= facearea&安培; facepad = 2及W = 256&安培; h = 256& q = 80' ,},]导出默认函数示例(){return(< ul classname =" divide-y divide-gray-200" {人。地图((人)=>(< li key = {person。电子邮件} classname =" py-4 flex">>>>>>>>>>>>>" h-10 w-10圆满" src = {人。Image} Alt ="" />>>" ml-3"&l lt; p classname =" text-sm字体媒体文本-Gray-900"> {人。姓名}< / p>< p classname =" text-sm text-gray-500"> {人。电子邮件}&lt ; / p>< / div><菜单AS =" div" classn

......